Closes#844.
When the session's cached working directory is renamed on disk and
a file is subsequently created at the old path (e.g. `mv orig renamed
&& touch orig`), every Bash tool invocation failed with
`ENOTDIR: not a directory, posix_spawn '/usr/bin/zsh'` (exit 126),
and `!`-prefixed commands silently failed. No recovery was possible
without restarting the session.
Root cause: the pre-spawn guard in `src/utils/Shell.ts:exec()` used
`realpath(cwd)` to detect a missing CWD. `realpath()` succeeds on
any existing path — file or directory — so a path that was replaced
with a regular file slipped past the check. spawn() was then called
with `cwd` pointing at a non-directory and failed with ENOTDIR.
Fix: replace `realpath()` with `stat().isDirectory()` for both the
primary CWD check and the `getOriginalCwd()` fallback check. When
the cached CWD is no longer a directory, fall back to the original
CWD (as before) and update state so subsequent tools recover
transparently.
Verification:
- Repro: `mkdir -p /tmp/x/orig && mv /tmp/x/orig /tmp/x/renamed
&& touch /tmp/x/orig`, then exec with stale cwd=/tmp/x/orig
- Before: exit 126, stderr "ENOTDIR: not a directory, posix_spawn"
- After: exit 0, cwd transparently recovered to originalCwd
- `bun test` — no new regressions (pre-existing model/provider
test failures are unrelated and present on main)
The banner provider branch tested model-name substrings (`/deepseek/`, `/kimi/`,
`/mistral/`, `/llama/`) before aggregator base-URL substrings (`/openrouter/`,
`/together/`, `/groq/`, `/azure/`). When running OpenRouter/Together/Groq with
vendor-prefixed model IDs (e.g. `deepseek/deepseek-chat`, `moonshotai/kimi-k2`,
`deepseek-r1-distill-llama-70b`), the banner mislabelled the provider.
Reorder: explicit env flags (NVIDIA_NIM, MINIMAX_API_KEY) and codex transport
win first; base-URL host checks run before rawModel fallback; rawModel only
fires when the base URL is generic/custom. Add unit tests covering the
aggregator × vendor-prefixed-model matrix plus direct-vendor regressions.
Closes#855
* fix: make OpenAI fallback context window configurable and support external lookup table
Unknown OpenAI-compatible models fell back to a hardcoded 128k constant,
causing auto-compact to fire prematurely on models with larger windows
(issue #635 follow-up). Two escape hatches are added without touching the
built-in table:
- CLAUDE_CODE_OPENAI_FALLBACK_CONTEXT_WINDOW (number): overrides the 128k
default for all unknown models.
- CLAUDE_CODE_OPENAI_CONTEXT_WINDOWS (JSON object): per-model overrides that
take precedence over the built-in OPENAI_CONTEXT_WINDOWS table; supports
the same provider-qualified and prefix-matching lookup as the built-in path.
- CLAUDE_CODE_OPENAI_MAX_OUTPUT_TOKENS (JSON object): same pattern for output
token limits.
This lets operators deploy new or private models without patching
openaiContextWindows.ts on every model release.
* docs: add new OpenAI context window env vars to .env.example
Document CLAUDE_CODE_OPENAI_FALLBACK_CONTEXT_WINDOW,
CLAUDE_CODE_OPENAI_CONTEXT_WINDOWS, and
CLAUDE_CODE_OPENAI_MAX_OUTPUT_TOKENS with usage examples.
Addresses reviewer feedback on PR #861.
---------
Co-authored-by: opencode <dev@example.com>
Gates three injection sites behind OPENCLAUDE_DISABLE_TOOL_REMINDERS:
- FileReadTool cyber-risk mitigation reminder (appended to every Read
result when the model is not in MITIGATION_EXEMPT_MODELS)
- todo_reminder attachment for TodoWrite usage
- task_reminder attachment for TaskCreate/TaskUpdate usage
All three reminders are model-only side-channel instructions the user
cannot see today. Users who want full transparency over what the model
receives can now opt out without patching dist/cli.mjs on every upgrade.
Default behavior is unchanged when the flag is unset.
Closes#809
mock.module('./teammate.js', ...) only declared getAgentName/getTeamName/
getTeammateColor. Bun applies module mocks process-globally and
mock.restore() does not undo them, so whenever another test file ran
after hookChains.integration.test.ts and reached the real teammate
module it received undefined for isTeammate/isPlanModeRequired/
getAgentId/getParentSessionId.
This surfaced in CI as intermittent failures in
src/commands/provider/provider.test.tsx (TextEntryDialog / wizard
remount / ProviderWizard hides Codex OAuth), because getDefaultAppState
in AppStateStore.ts calls teammateUtils.isTeammate().
Match the mock surface to the real teammate.ts exports so downstream
consumers keep working even after the integration test pollutes the
module cache. Keeps the same behavioral overrides this test needed.
Closes#839
- Add exponential backoff retry to DuckDuckGo adapter (3 attempts with
jitter) to handle transient rate-limiting and connection errors.
- Add native fetch() fallback in WebFetch when axios hangs with custom
DNS lookup in bundled contexts.
- Prevent broken native-path fallback for web search on OpenAI shim
providers (minimax, moonshot, nvidia-nim, etc.) that do not support
Anthropic's web_search_20250305 tool.
- Cherry-pick existing fixes:
- a48bd56: cover codex/minimax/nvidia-nim in getSmallFastModel()
- 31f0b68: 45s budget + raw-markdown fallback for secondary model
- 446c1e8: sparse Codex /responses payload parsing
- ae3f0b2: echo reasoning_content on assistant tool-call messages
- Fix domainCheck.test.ts mock modules to include isFirstPartyAnthropicBaseUrl
and isGithubNativeAnthropicMode exports.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
Non-Anthropic / non-codex providers (minimax, kimi, generic OpenAI-compatible)
fell through to the DDG adapter when no paid search key was configured. DDG's
scraper is blocked on most IPs, so web_search surfaced an opaque "anomaly in
the request" error. Catch that response in the DDG provider and rethrow with
the exact env vars that would unblock the tool, or the option to switch to a
native-search provider.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Kimi / Moonshot's chat completions endpoint requires that every assistant
message carrying tool_calls also carry reasoning_content when the
"thinking" feature is active. When an agent sends prior-turn assistant
history back (standard multi-turn / subagent / Explore patterns), the
shim previously stripped the thinking block:
case 'thinking':
case 'redacted_thinking':
// Strip thinking blocks for OpenAI-compatible providers.
break
That's correct for providers that would mis-interpret serialized
<thinking> tags, but Moonshot validates the schema strictly and rejects
with:
API Error: 400 {"error":{"message":"thinking is enabled but
reasoning_content is missing in assistant tool call message at
index N","type":"invalid_request_error"}}
Reproducer: launch with Kimi profile, run any tool-using command
(Explore, Bash, etc.) — every request after the first 400s.
Fix: in convertMessages(), when the per-request flag
preserveReasoningContent is set (only for Moonshot baseUrls today),
attach the original thinking block's text as reasoning_content on the
outgoing OpenAI-shaped assistant message. Other providers continue to
strip (unknown-field rejection risk).
OpenAIMessage type grows a reasoning_content?: string field.
convertMessages() accepts an options object and threads the flag
through; the only call site (_doOpenAIRequest) gates via
isMoonshotBaseUrl(request.baseUrl).
Tests (openaiShim.test.ts):
- Moonshot: echoes reasoning_content on assistant tool-call messages
(regression for the reported 400)
- non-Moonshot providers do NOT receive reasoning_content (guards
against leaking the field to strict-parse endpoints)
Full suite: 1195/1195 pass under --max-concurrency=1. PR scan clean.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
* Integrate request logging and streaming optimizer
- Add logApiCallStart/End for API request tracking with correlation IDs
- Add streaming state tracking with processStreamChunk
- Flush buffer and log stream stats at stream end
- Resolve merge conflict with main branch
* feat: add streaming optimizer and structured request logging
* fix: address PR review feedback
- Remove buffering from streamingOptimizer - now purely observational
- Use logForDebugging instead of console.log for structured logging
- Remove dead code (streamResponse, bufferedStreamResponse, etc.)
- Use existing logging infrastructure instead of raw console.log
- Keep only used functions: createStreamState, processStreamChunk, getStreamStats
* test: add unit tests for requestLogging and streamingOptimizer
- streamingOptimizer.test.ts: 6 tests for createStreamState, processStreamChunk, getStreamStats
- requestLogging.test.ts: 6 tests for createCorrelationId, logApiCallStart, logApiCallEnd
* fix: correct durationMs test to be >= 0 instead of exactly 0
* fix: address PR #703 blockers and non-blockers
1. BLOCKER FIX: Skip clone() for streaming responses
- Only call response.clone() + .json() for non-streaming requests
- For streaming, usage comes via stream chunks anyway
2. NON-BLOCKER: Document dead code in flushStreamBuffer
- Added comment explaining it's a no-op kept for API compat
3. NON-BLOCKER: vi.mock in tests - left as-is (test framework issue)
* fix: address all remaining non-blockers for PR #703
1. Remove dead code: flushStreamBuffer call and unused import
2. Fix test for Bun: remove vi.mock, use simple no-throw tests
The test "never returns negative even for unknown 3P models (issue #635)"
asserted that getEffectiveContextWindowSize() returns >= 33_000 for an
unknown 3P model under the OpenAI shim. That specific number assumes
reservedTokensForSummary = 20_000 (MAX_OUTPUT_TOKENS_FOR_SUMMARY), which
holds only when the tengu_otk_slot_v1 GrowthBook flag is disabled.
When the flag is ON — which is the case in CI but not always locally —
getMaxOutputTokensForModel() caps the model's default output at
CAPPED_DEFAULT_MAX_TOKENS (8_000). Then reservedTokensForSummary = 8_000,
floor = 8_000 + 13_000 = 21_000, and the test fails with 21_000 < 33_000.
The test reliably passes locally and reliably fails in CI, manifesting as
the intermittent PR-check failure.
Fix: relax the lower bound to 21_000 (cap-enabled worst case), which is
still well above zero — preserving the anti-regression intent of
issue #635 (no infinite auto-compact from a negative effective window)
without binding the test to GrowthBook flag state.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
getUserSpecifiedModelSetting() decides which env var to consult based on
the active provider. The check included openai and github but omitted
codex, nvidia-nim, and minimax — even though all three use the OpenAI
shim transport and get their model routing via CLAUDE_CODE_USE_OPENAI=1
+ OPENAI_MODEL (set by applyProviderProfileToProcessEnv).
Concrete failure: user switches from Moonshot profile (which persisted
settings.model='kimi-k2.6') to the Codex profile. The new profile
correctly writes OPENAI_MODEL=codexplan + base URL to
chatgpt.com/backend-api/codex. Startup banner reflects Codex / gpt-5.4
correctly. But at request time getUserSpecifiedModelSetting() returns
early for provider='codex' (not in the env-consult list), falls through
to the stale settings.model='kimi-k2.6', and the Codex API rejects:
API Error 400: "The 'kimi-k2.6' model is not supported when using
Codex with a ChatGPT account."
Fix: extract an isOpenAIShimProvider flag covering openai|codex|github|
nvidia-nim|minimax — all providers that set OPENAI_MODEL as their model
env var. The Gemini and Mistral branches stay as-is (they use
GEMINI_MODEL / MISTRAL_MODEL).
Five regression tests pin the fix for each OpenAI-shim provider plus
guard tests for openai and github that already worked.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
Adds Atomic Chat as a first-class preset inside the in-session /provider
slash command, mirroring the Ollama auto-detect flow. Picking it probes
127.0.0.1:1337/v1/models, lists loaded models for direct selection, and
falls back to "Enter manually" / "Back" when the server is unreachable
or no models are loaded. README updated to reflect the new setup path.
Made-with: Cursor
* fix(provider): saved profile ignored when stale CLAUDE_CODE_USE_* in shell
Users reported "my saved /provider profile isn't picked up at startup —
the banner shows gpt-4o / api.openai.com even though I saved Moonshot".
Root cause: applyActiveProviderProfileFromConfig() bailed out whenever
hasProviderSelectionFlags(processEnv) was true — i.e. whenever ANY
CLAUDE_CODE_USE_* flag was present. But a bare `CLAUDE_CODE_USE_OPENAI=1`
with no paired OPENAI_BASE_URL / OPENAI_MODEL is almost always a stale
shell export left over from a prior manual setup, not genuine startup
intent. Respecting it skipped the saved profile and let StartupScreen.ts
fall through to the hardcoded `gpt-4o` / `https://api.openai.com/v1`
defaults — the exact symptom users see.
Fix: narrow the guard from "any flag set" to "flag set AND at least one
concrete config value (BASE_URL, MODEL, or API_KEY)". A bare stale flag
no longer blocks the saved profile. A real shell selection (flag + URL
or flag + model) still wins, preserving the "explicit startup intent
overrides saved profile" contract.
New helper: hasCompleteProviderSelection(env). Per-provider check for a
paired concrete value. Bedrock/Vertex/Foundry keep the flag-alone
semantic since they rely on ambient AWS/GCP credentials rather than env
config.
Three new tests cover the bug and the two counter-cases:
- bare USE flag → profile applies (fixes the bug)
- USE flag + BASE_URL → profile blocked (preserves explicit intent)
- USE flag + MODEL → profile blocked (preserves explicit intent)
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(provider): don't overlay stale legacy profile on plural-managed env
Second half of the "saved profile not picked up in banner" bug. The prior
commit fixed the guard that prevented applyActiveProviderProfileFromConfig()
from firing when a stale CLAUDE_CODE_USE_* flag was in the shell. But even
when the plural system applies correctly, buildStartupEnvFromProfile() was
then loading the legacy .openclaude-profile.json AND overwriting the
plural-managed env with whatever that file contained.
addProviderProfile() (the call path the /provider preset picker uses) does
NOT sync the legacy file, so a user who went:
manual setup: CLAUDE_CODE_USE_OPENAI=1 + OPENAI_MODEL=gpt-4o
→ writes .openclaude-profile.json as { openai, gpt-4o, ... }
/provider: add Moonshot preset, mark active
→ writes plural config; legacy file UNCHANGED
would see startup reliably apply Moonshot env first, then get it clobbered
by the stale legacy file. Banner shows gpt-4o / api.openai.com while
runtime ends up with the correct env via a different code path — exactly
the user-reported symptom.
Fix: in buildStartupEnvFromProfile, when the plural system has already
set env (CLAUDE_CODE_PROVIDER_PROFILE_ENV_APPLIED === '1'), skip the
legacy-file overlay entirely and return processEnv unchanged. Legacy is
now strictly a first-run / fallback path for users who haven't adopted
the plural system.
Also removes the stripped-then-rebuilt env construction that was part of
the old overlay path — no longer needed.
Test updates:
- Replaced "lets saved startup profile override profile-managed env"
(encoded the old broken behavior) with a regression test that pins
the new semantic: plural env survives when legacy is stale.
- Added "falls back to legacy when plural hasn't applied" to pin the
first-run path still works.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
---------
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
First-run users with a credential already exported (ANTHROPIC_API_KEY,
OPENAI_API_KEY, etc.) currently still have to navigate the provider picker
or set CLAUDE_CODE_USE_* flags manually. Selecting the right provider from
ambient state should be automatic.
New module src/utils/providerAutoDetect.ts:
- detectProviderFromEnv() — synchronous env scan in a deterministic priority
order (anthropic → codex → github → openai → gemini → mistral → minimax).
Also detects Codex via ~/.codex/auth.json presence.
- detectLocalService() — parallel probes for Ollama (:11434) and LM Studio
(:1234), with honoring of OLLAMA_BASE_URL / LM_STUDIO_BASE_URL overrides.
Short 1.2s default timeout so first-run latency stays low when no local
service is running.
- detectBestProvider() — orchestrator. Env scan short-circuits the probe;
only hits the network when env has nothing.
All detection paths are side-effect-free: returns a DetectedProvider
descriptor describing what was found and why. Callers decide whether to
apply it (gated on hasExplicitProviderSelection() / profile file existence)
and how to hydrate the launch env.
Codex auth-file check is injectable (hasCodexAuth option) so tests are
hermetic from the dev machine's ~/.codex/auth.json state.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
* feat: add thinking token tracking and historical analytics
- extractThinkingTokens(): separate thinking from output tokens
- TokenUsageTracker class for historical analytics
- Track: cache hit rate, most used model, requests per hour/day
- Analytics: average tokens per request, totals
- Add tests (7 passing)
PR 4B: Features 1.10 + 1.11
* refactor: extract thinking and analytics to separate files
- Create thinkingTokenExtractor.ts with ThinkingTokenAnalyzer
- Create tokenAnalytics.ts with TokenUsageTracker
- Add production-grade methods and tests
- Update test imports
Fixes#774. When tool_result content contains multiple text blocks,
they were serialized as arrays instead of strings, causing DeepSeek
to reject the request with 400 error.
Changes:
- convertToolResultContent: collapse all-text arrays to joined string
- convertContentBlocks: defensive collapse for user/assistant messages
- Arrays with images are preserved (not collapsed)
Tests: 3 new tests added, 53 pass, 0 fail
Co-authored-by: nick.mesen <nickmesen@users.noreply.github.com>
Most everyday turns ("ok", "thanks", "yep go ahead", "what does that do?")
get no measurable quality improvement from Opus-tier models over Haiku-tier,
but cost ~10x more and stream slower. Smart routing opts a user into
automatically routing obviously-simple turns to a cheaper model while
keeping the strong model for anything non-trivial.
New module src/services/api/smartModelRouting.ts:
- routeModel(input, config) → { model, complexity, reason }
- Pure primitive: no env reads, no state, caller supplies everything.
- Config is opt-in (enabled: false by default).
Routes to strong (conservative) when ANY of:
- First turn of session (task-setup is worth the quality)
- Code fence or inline code span present
- Reasoning/planning keyword (plan, design, refactor, debug, architect,
investigate, root cause, etc. — 20+ anchors)
- Multi-paragraph input
- Over char/word cutoff (defaults: 160 chars, 28 words; matches hermes)
Routes to simple only for clearly-trivial chatter.
Decision includes a reason string for a future UI indicator that shows
which tier handled the turn.
Integration into query path is intentionally deferred to a follow-up PR so
the heuristics can be reviewed and tuned in isolation first.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
* feat(provider): first-class Moonshot (Kimi) direct-API support
Moonshot's direct API (api.moonshot.ai/v1) is OpenAI-compatible and works
today via the generic OpenAI shim, including the reasoning_content channel
that Kimi returns alongside the user-visible content. But the UX was rough:
unknown context window triggered the conservative 128k fallback + a warning,
and the provider displayed as "Local OpenAI-compatible".
Makes Moonshot a recognized provider:
- src/utils/model/openaiContextWindows.ts: add the Kimi K2 family and
moonshot-v1-* variants to both the context-window and max-output tables.
Values from Moonshot's model card — K2.6 and K2-thinking are 256K,
K2/K2-instruct are 128K, moonshot-v1 sizes are embedded in the model id.
- src/utils/providerDiscovery.ts: recognize the api.moonshot.ai hostname
and label it "Moonshot (Kimi)" in the startup banner and provider UI.
Users can now launch with:
CLAUDE_CODE_USE_OPENAI=1 \
OPENAI_BASE_URL=https://api.moonshot.ai/v1 \
OPENAI_API_KEY=sk-... \
OPENAI_MODEL=kimi-k2.6 \
openclaude
and get accurate compaction + correct labeling + correct max_tokens out
of the box.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(openai-shim): Moonshot API compatibility — max_tokens + strip store
Moonshot's direct API (api.moonshot.ai and api.moonshot.cn) uses the
classic OpenAI `max_tokens` parameter, not the newer `max_completion_tokens`
that the shim defaults to. It also hasn't published support for `store`
and may reject it on strict-parse — same class of error as Gemini's
"Unknown name 'store': Cannot find field" 400.
- Adds isMoonshotBaseUrl() that recognizes both .ai and .cn hosts.
- Converts max_completion_tokens → max_tokens for Moonshot requests
(alongside GitHub / Mistral / local providers).
- Strips body.store for Moonshot requests (alongside Mistral / Gemini).
Two shim tests cover both the .ai and .cn hostnames.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix: null-safe access on getCachedMCConfig() in external builds
External builds stub src/services/compact/cachedMicrocompact.ts so
getCachedMCConfig() returns null, but two call sites still dereferenced
config.supportedModels directly. The ?. operator was in the wrong place
(config.supportedModels? instead of config?.supportedModels), so the null
config threw "Cannot read properties of null (reading 'supportedModels')"
on every request.
Reproduces with any external-build provider (notably Kimi/Moonshot just
enabled in the sibling commits, but equally DeepSeek, Mistral, Groq,
Ollama, etc.):
❯ hey
⏺ Cannot read properties of null (reading 'supportedModels')
- prompts.ts: early-return from getFunctionResultClearingSection() when
config is null, before touching .supportedModels.
- claude.ts: guard the debug-log jsonStringify with ?. so the log line
never throws.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(startup): show "Moonshot (Kimi)" on the startup banner
The startup-screen provider detector had regex branches for OpenRouter,
DeepSeek, Groq, Together, Azure, etc., but nothing for Moonshot. Remote
Moonshot sessions fell through to the generic "OpenAI" label —
getLocalOpenAICompatibleProviderLabel() only runs for local URLs, and
api.moonshot.ai / api.moonshot.cn are not local.
Adds a Moonshot branch matching /moonshot/ in the base URL OR /kimi/ in
the model id. Now launches with:
OPENAI_BASE_URL=https://api.moonshot.ai/v1 OPENAI_MODEL=kimi-k2.6
display the Provider row as "Moonshot (Kimi)" instead of "OpenAI".
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* refactor(provider): sort preset picker alphabetically; Custom at end
The /provider preset picker was in ad-hoc order (Anthropic, Ollama,
OpenAI, then a jumble of third-party / local / codex / Alibaba / custom /
nvidia / minimax). Hard to scan when you know the provider name you want.
Sorts the list alphabetically by label A→Z. Pins "Custom" to the end —
it's the catch-all / escape hatch so it's scanned last, not shuffled into
the alphabetical run where a user looking for a named provider might
grab it by mistake. First-run-only "Skip for now" stays at the very
bottom, after Custom.
Test churn:
- ProviderManager.test.tsx: four tests hardcoded press counts (1 or 3 'j'
presses) that broke when targets moved. Replaces them with a
navigateToPreset(stdin, label) helper driven from a declared
PRESET_ORDER array, so future list edits only update the array.
- ConsoleOAuthFlow.test.tsx: the 13-row test frame only renders the first
~13 providers. "Ollama", "OpenAI", "LM Studio" sentinels moved below
the fold; swap them for alphabetically-early providers still visible
in-frame ("Azure OpenAI", "DeepSeek", "Google Gemini"). Test intent
(picker opened with providers listed) is preserved.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
---------
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
* feat: add model caching and benchmarking utilities
- Add modelCache.ts for disk caching of model lists
- Add benchmark.ts for testing model speed/quality
* fix: address review feedback - async fs, multi-provider support, error handling
* feat: add /benchmark slash command and unit tests
* feat: add /benchmark slash command and unit tests
* feat: enable 16 additional feature flags in open build
Activate features whose source is fully available in the mirror and
that have no Anthropic-internal infrastructure dependencies:
UI/UX: MESSAGE_ACTIONS, HISTORY_PICKER, QUICK_SEARCH, HOOK_PROMPTS
Reasoning: ULTRATHINK, TOKEN_BUDGET, SHOT_STATS
Agents: FORK_SUBAGENT, VERIFICATION_AGENT, MCP_SKILLS
Memory: EXTRACT_MEMORIES, AWAY_SUMMARY
Optimization: CACHED_MICROCOMPACT, PROMPT_CACHE_BREAK_DETECTION
Safety: TRANSCRIPT_CLASSIFIER
Debug: DUMP_SYSTEM_PROMPT
Also reorganize featureFlags into documented sections (disabled/upstream/new)
with inline comments explaining each flag's purpose.
* feat: add centralized GrowthBook defaults map for open build
Add _openBuildDefaults in the GrowthBook stub (no-telemetry-plugin.ts)
with all 66 runtime feature keys, organized by category with inline
comments describing each flag's purpose.
Override tengu_sedge_lantern (AWAY_SUMMARY) and tengu_hive_evidence
(VERIFICATION_AGENT) to true so these features work out of the box
without requiring manual ~/.claude/feature-flags.json setup.
Priority: feature-flags.json > _openBuildDefaults > upstream default
* feat: replace refusal language with positive security guidance
Remove refusal instructions from CYBER_RISK_INSTRUCTION since they are
redundant for Anthropic models (applied server-side) and useless for
uncensored models in multi-provider setups. Keep positive guidance for
security testing contexts and add red teaming support.
* Revert "feat: replace refusal language with positive security guidance"
This reverts commit 0463676a8f.
* fix: add EXTRACT_MEMORIES runtime gate overrides to open-build defaults
EXTRACT_MEMORIES was enabled at build-time but its runtime GrowthBook
gates (tengu_passport_quail, tengu_coral_fern) still defaulted to false,
preventing the feature from activating. Add both keys to
_openBuildDefaults so memory extraction works out of the box.
Also adds test coverage for _openBuildDefaults precedence behavior.
* docs: update GrowthBook runtime keys catalog to 88 keys
Expand the reference catalog in no-telemetry-plugin.ts from ~62 to 88
unique keys, covering all tengu_* call sites found in src/. Adds 27
previously undocumented keys including VSCode gates, dynamic configs
(auto-mode, cron, bridge), security gates, and KAIROS cron keys.
Adds "not exhaustive" disclaimer as suggested by Copilot reviewer.
Reorganizes categories with section dividers for readability.
* fix(security): harden project settings trust boundary + MCP sanitization
- Sanitize MCP tool result text with recursivelySanitizeUnicode() to prevent
Unicode injection via malicious MCP servers (tool definitions and prompts
were already sanitized, but tool call results were not)
- Read sandbox.enabled only from trusted settings sources (user, local, flag,
policy) — exclude projectSettings to prevent malicious repos from silently
disabling the sandbox via .claude/settings.json
- Disable git hooks in plugin marketplace clone/pull/submodule operations
with core.hooksPath=/dev/null to prevent code execution from cloned repos
- Remove ANTHROPIC_FOUNDRY_API_KEY from SAFE_ENV_VARS to prevent credential
injection from project-scoped settings without trust verification
- Add ssrfGuardedLookup to WebFetch HTTP requests to block DNS rebinding
attacks that could reach cloud metadata or internal services
Security: closes trust boundary gap where project settings could override
security-critical configuration. Follows the existing pattern established
by hasAllowBypassPermissionsMode() which already excludes projectSettings.
Co-authored-by: auriti <auriti@users.noreply.github.com>
* fix(security): remove unauthenticated file-based permission polling
Remove the legacy file-based permission polling from useSwarmPermissionPoller
that read from ~/.claude/teams/{name}/permissions/resolved/ — an unauthenticated
directory where any local process could forge approval files to auto-approve
tool uses for swarm teammates.
The file polling was dead code:
- The useSwarmPermissionPoller() hook was never mounted by any component
- resolvePermission() (the file writer) was never imported outside its module
- Permission responses are delivered exclusively via the mailbox system:
Leader: sendPermissionResponseViaMailbox() → writeToMailbox()
Worker: useInboxPoller → processMailboxPermissionResponse()
Changes:
- Remove file polling loop, processResponse(), and React hook imports from
useSwarmPermissionPoller.ts (now a pure callback registry module)
- Mark 7 file-based functions as @deprecated in permissionSync.ts
- Add 4 regression tests verifying the removal
No exported functions removed — only deprecated. All 5 consumer modules
verified: they import only mailbox-based functions that remain unchanged.
---------
Co-authored-by: auriti <auriti@users.noreply.github.com>
* feat(api): compress old tool_result content for small-context providers
Adds a shim-layer pass that tiers tool_result content by age on
providers
with small effective context windows (Copilot gpt-4o 128k, Mistral,
Ollama). Recent turns remain full; mid-tier results are truncated to
2k
chars; older results are replaced with a stub that preserves tool name
and arguments so the model can re-invoke if needed.
Tier sizes auto-tune via getEffectiveContextWindowSize, same
calculation
used by auto-compact. Reuses COMPACTABLE_TOOLS and
TOOL_RESULT_CLEARED_MESSAGE to complement (not duplicate)
microCompact.
Configurable via /config toolHistoryCompressionEnabled.
Addresses active-session context accumulation on Copilot where
microCompact's time-based trigger never fires, which surfaces as
"tools appearing in a loop" and prompt_too_long errors after ~15
turns.
* fix: config tool history
Updates both the model config mappings (configs.ts) and the runtime
fallback in getDefaultOpusModel() (model.ts) so Gemini mode no longer
falls back to the discontinued preview model when GEMINI_MODEL is unset.
Fixes#398
ProviderManager was blocking the main thread with synchronous file I/O
on mount (useState initializer), activation (setActiveProviderProfile),
and refresh (getProviderProfiles). This caused noticeable lag on Windows
where disk I/O can be slow due to antivirus scans, NTFS metadata, or
cache misses.
Changes to ProviderManager:
- Deferred initialization: useState now starts empty, loads via queueMicrotask
- Added isInitializing state with loading UI
- refreshProfiles() now defers reads via queueMicrotask
- activateSelectedProvider() now defers writes via queueMicrotask
- Memoized menuOptions array to prevent re-renders during navigation
Note: ProviderChooser useMemo change was reverted as it's dead code
(ProviderWizard is not used in production - /provider uses ProviderManager).
Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com>
* fix: rename .claude.json to .openclaude.json with legacy fallback
Rename the global config file from ~/.claude.json to ~/.openclaude.json,
following the same migration pattern as the config directory
(~/.claude → ~/.openclaude).
- getGlobalClaudeFile() now prefers .openclaude.json; falls back to
.claude.json only if the legacy file exists and the new one does not
- Add .openclaude.json to filesystem permissions allowlist (keep
.claude.json for legacy file protection)
- Update all comment/string references from ~/.claude.json to
~/.openclaude.json across 12 files
New installs get .openclaude.json from the start. Existing users
continue using .claude.json until they rename it (or a future explicit
migration).
* test: add unit tests for getGlobalClaudeFile migration branches
Covers the three cases:
- new install (neither file exists) → .openclaude.json
- existing user (only legacy .claude.json exists) → .claude.json
- migrated user (both files exist) → .openclaude.json
---------
Co-authored-by: Zartris <14197299+Zartris@users.noreply.github.com>
* feat: native Anthropic API mode for Claude models on GitHub Copilot
When using Claude models through GitHub Copilot, automatically switch from
the OpenAI-compatible shim to Anthropic's native messages API format.
The Copilot proxy (api.githubcopilot.com) supports Anthropic's native API
for Claude models. This enables cache_control blocks to be sent and
honoured, allowing explicit prompt caching control (as opposed to relying
solely on server-side auto-caching).
Changes:
- Add isGithubNativeAnthropicMode() in providers.ts that auto-enables when
the resolved model starts with "claude-" and the GitHub provider is active
- Create a native Anthropic client in client.ts using the GitHub base URL
and Bearer token authentication when native mode is detected
- Enable prompt caching in claude.ts for native GitHub mode so cache_control
blocks are sent (previously only allowed for firstParty/bedrock/vertex)
- CLAUDE_CODE_GITHUB_ANTHROPIC_API=1 env var to force native mode for any
model
Benefits:
- Proper Anthropic message format (no lossy OpenAI translation)
- Explicit cache_control blocks for fine-grained caching control
- Potentially better Claude model behaviour with native format
Related: #515
* fix: scope force flag to Claude models and add isGithubNativeAnthropicMode tests
- CLAUDE_CODE_GITHUB_ANTHROPIC_API=1 now returns false for non-Claude models
(force flag still useful for aliases like 'github:copilot' with no model
resolved yet, where it returns true when model is empty)
- Add 7 focused tests covering mode detection: off without GitHub provider,
auto-detect via OPENAI_MODEL and resolvedModel, non-Claude model rejection,
and force-flag behaviour for claude/non-claude/no-model cases
* fix: detect github:copilot:claude- compound format, remove force flag
OPENAI_MODEL for GitHub Copilot uses the format 'github:copilot:MODEL'
(e.g. 'github:copilot:claude-sonnet-4'), which does not start with 'claude-'.
Auto-detection now handles both bare model names and the compound format.
The CLAUDE_CODE_GITHUB_ANTHROPIC_API force flag is removed: with proper
compound-format detection there is no remaining gap it could fill, and
keeping a broad override flag without a concrete use case invites misuse.
Tests updated to cover the compound format, generic alias (false), and
non-Claude compound model (github:copilot:gpt-4o → false).
* fix: use includes('claude-') for model detection, remove force flag
Detection was broken for the standard GitHub Copilot compound format
'github:copilot:claude-sonnet-4' which does not start with 'claude-'.
Using includes('claude-') handles bare names, compound names, and any
future variants without needing updates.
The CLAUDE_CODE_GITHUB_ANTHROPIC_API force flag is removed as it was
a workaround for the broken detection, not a genuine use case.
---------
Co-authored-by: Zartris <14197299+Zartris@users.noreply.github.com>
Reasoning models (MiniMax M2.7, GLM-4.5/5, DeepSeek, Kimi K2) inline
chain-of-thought inside <think>...</think> tags in the content field
rather than using the reasoning_content channel. The prior phrase-matching
sanitizer (looksLikeLeakedReasoningPrefix) only caught English-prose
preambles like "I should"/"the user asked", missed tag-based leaks
entirely, and risked false-stripping legitimate assistant output.
Replace with a structural tag-based approach (same pattern as hermes-agent):
- createThinkTagFilter() — streaming state machine that buffers partial
tags across SSE delta boundaries (<th| + |ink>), so tags split mid-chunk
still parse correctly.
- stripThinkTags() — whole-text cleanup for non-streaming responses and
as a safety net. Handles closed pairs, unterminated opens at block
boundaries, and orphan tags.
- Recognizes think, thinking, reasoning, thought, REASONING_SCRATCHPAD
case-insensitively, including tags with attributes.
- False-negative bias: flush() discards buffered partial tags at stream
end rather than leaking them.
Existing phrase-based shim tests updated to exercise the actual <think>
tag leak. Added regression tests confirming legitimate prose starting
with "I should..." is preserved (the old sanitizer's main false-positive).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When set, disables strict schema normalization for non-Gemini providers.
Useful for OpenAI-compatible endpoints that reject MCP tools with complex
optional params (e.g. list[dict]) with "Extra required key ... supplied"
errors.
* fix: remove cached mcpClient in diagnostic tracking to prevent stale references
Resolves TODO comment about not caching the connected mcpClient since it can change.
Changes:
- Remove cached mcpClient field from DiagnosticTrackingService
- Add currentMcpClients storage to track active clients
- Update beforeFileEdited, getNewDiagnostics, and ensureFileOpened to accept client parameter
- Add backward-compatible methods to maintain existing API
- Update all callers to use new methods
- Add comprehensive test coverage
This prevents using stale MCP client references during reconnections,
making diagnostic tracking more reliable.
Fixes #TODO
* docs: add my contributions section to README
Add fork-specific section highlighting:
- Diagnostic tracking enhancement (PR #727)
- Technical skills demonstrated
- Links to original project and my work
- Professional contribution showcase
* revert: remove README.md contributions section to comply with reviewer request
- Remove 'My Fork & Contributions' section from README.md
- Keep README.md focused on original project documentation
- Maintain clean, project-focused README as requested by reviewer
* fix(api): drop orphan tool results to satisfy Mistral/OpenAI strict role sequence
* test: add test for orphan tool results and restore gemini comments
Problem: After auto-compaction with DeepSeek models (e.g., deepseek-chat),
the status line displayed ~16% remaining until next auto-compact, but users
expected ~30% (since compaction reduces usage to roughly half of the full
128k context).
Root cause: calculateTokenWarningState() used the auto-compaction threshold
(effectiveContextWindow - 13k buffer) as the denominator for percentLeft.
For DeepSeek-chat:
- Raw context: 128,000
- Effective: 119,808 (128k - 8,192 output reservation)
- Threshold: 106,808 (effective - 13k buffer)
At 90k usage:
- Old: (106,808 - 90k) / 106,808 ≈ 16%
- Expected: (128,000 - 90k) / 128,000 ≈ 30%
Fix: Change percentLeft calculation to use raw context window from
getContextWindowForModel() as denominator, while keeping threshold-based
warnings/triggers unchanged. This makes the displayed percentage show
remaining capacity relative to the model's full context size.
Impact:
- UI now shows correct % of total context remaining
- Auto-compaction trigger point unchanged (still ~90% of effective window)
- All other threshold calculations unaffected
Testing:
- Manual verification: DeepSeek-chat at 90k tokens shows 30% remaining (was 16%)
- Manual verification: Threshold still triggers at ~106k tokens
- Build succeeds: npm run build
- No breaking changes: Callers only depend on percentLeft for display; threshold logic unchanged
Fixes the user-reported discrepancy for DeepSeek and other OpenAI-compatible models.
* fix(mcp): sync required array with properties in tool schemas
MCP servers can emit schemas where the required array contains keys
not present in properties. This causes API 400 errors:
"Extra required key 'X' supplied."
- Add sanitizeSchemaRequired() to filter required arrays
- Apply it to MCP tool inputJSONSchema before sending to API
- Also fix filterSwarmFieldsFromSchema to update required after
removing properties
Fixes#525
* test: add MCP schema required sanitization test
* add mistral and gemini provider type for profile provider field
* load latest locally selected
* env variables take precedence over json save
* add gemini context windows and fix gemini defaulting for env
* load on startup fix
* fix failing tests
* clarify test message
* fix variable mismatches
* fix failing test
* delete keys and set profile.apiKey for mistral and gemini
* switch model as well when switching provider
* set model when adding a new model
Previously, the startup intro screen always displayed
'https://api.anthropic.com' as the endpoint for Anthropic provider,
even when a custom endpoint was configured via ANTHROPIC_BASE_URL.
This fix reads ANTHROPIC_BASE_URL from environment and displays the
actual configured endpoint, providing accurate information to users
about where their API requests will be sent (proxy gateways, staging,
custom Anthropic-compatible APIs).
Also adds isLocal detection for local endpoints to show appropriate
visual indicator in the startup banner.
Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com>
This commit fixes a crash in the CLI that occurs when navigating to the /help commands tab. The issue happens because the truncate function receives an undefined value for the str parameter if a command lacks a description, causing the .indexOf() method to throw an exception. To resolve this, an early return check was added at the beginning of the function to gracefully handle empty values and prevent the UI from crashing.
When returning to the provider manager menu after completing an action
(add, edit, delete, set active, etc.), the cursor now lands on "Done"
instead of the first option ("Add provider"). This prevents accidental
re-entry into the same action if the user presses Enter quickly.
On initial /provider invocation, the cursor still starts on the first
option ("Add provider") as expected.
Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com>
Document the new `ollama launch openclaude` command as a shortcut
for running OpenClaude through a local Ollama instance. This is
now supported in Ollama's launch system and handles all environment
variable setup automatically — no manual env vars needed.
Changes:
- README.md: Add "Using Ollama's launch command" section after the
manual Ollama env var setup, and update the provider table to
list `ollama launch` as a setup path for Ollama
- docs/advanced-setup.md: Add `ollama launch` as the recommended
method at the top of the Ollama section, with the manual env var
approach kept below as an alternative
Add a Claude Code-like chat experience to the VS Code extension with:
- Streaming chat panel (sidebar + editor tab) with markdown rendering
- Tool use visualization with inline diffs (replace/with display)
- Session history browser with JSONL transcript parsing
- Thinking block indicator with elapsed time and token count
- Clickable file paths that open in the editor
- Permission mode setting (acceptEdits default)
- Multi-turn conversation support via NDJSON stream-json protocol
- Status bar with live activity indicators
- Ctrl+Shift+L keybinding to open chat panel
Made-with: Cursor
Co-authored-by: henriquepasquini2 <henriquepasquini2@users.noreply.github.com>
* fix: OAuth tokens secure storage for Windows & Linux
* fix(mcp): MCP Tool Re-exposure & Strict Input Validation
Fixes the MCP re-exposure bug by correctly handling tool deduplication, input validation with Ajv, and structured output (including images). Also disables experimental API betas by default to prevent 500 errors on external accounts.
* fix(mcp): skip official registry prefetch in non-first-party mode
Prevents unnecessary calls to Anthropic's MCP registry when using other API providers.
* fix(cli): disable experimental API betas by default
This prevents 500 errors from Anthropic's API when tool-calling with non-Anthropic accounts or models that don't support certain beta features.
* fix: issues raised in the PR review for #675
* test: add tests for provider model env updates and multi-model profiles
Add comprehensive tests covering:
- OPENAI_MODEL/ANTHROPIC_MODEL env updates on provider activation
- Cross-provider type switches (openai ↔ anthropic) clearing stale env
- Multi-model profile activation using only the first model for env vars
- Model options cache population from comma-separated model lists
- getProfileModelOptions generating correct ModelOption arrays
* feat: multi-model provider support and model auto-switch
Support comma-separated model names in provider profiles (e.g.
"glm-4.7, glm-4.7-flash"). The first model is used as default on
activation; all models appear in the /model picker for easy switching.
When switching active providers, the session model now automatically
updates to the new provider's first model. The multi-model list is
preserved across switches and /model selections.
Changes:
- Add parseModelList, getPrimaryModel, hasMultipleModels utilities
with full test coverage (19 tests)
- Use getPrimaryModel when applying profiles to process.env so only
the primary model is set in OPENAI_MODEL/ANTHROPIC_MODEL
- Update ProviderManager UI to hint at multi-model syntax and show
model count in provider list summaries
- Populate model options cache from multi-model profiles on activation
so all models appear in /model picker regardless of base URL type
- Guard persistActiveProviderProfileModel against overwriting
comma-separated lists: models already in the profile are session
selections, not profile edits
- Set AppState.mainLoopModel to the actual model string on provider
switch so Anthropic profiles use the configured model instead of
falling back to the built-in default
* fix: only show profile models when provider profile env is applied
Guard the profile model picker options behind a
PROFILE_ENV_APPLIED check. getActiveProviderProfile() has a
?? profiles[0] fallback that returns the first profile even when
no profile is explicitly active, causing users with inactive
profiles to lose all standard model options (Opus, Haiku, etc.)
from the /model picker.
* fix: show all model names for profiles with 3 or fewer models
Instead of a summary format for multi-model profiles, display all
model names when there are 3 or fewer. Only use the "+ N more"
format for profiles with 4+ models.
* fix: preserve standard model options in picker alongside profile models
The previous implementation used an early return that replaced all
standard picker options (Opus, Haiku, Sonnet for Anthropic; Codex/GPT
models for OpenAI) with only the profile's custom models.
Changes:
- Collect profile models into a shared array instead of early returning
- Append profile models to firstParty path (Opus + Haiku + Sonnet + custom)
- Append profile models to PAYG 3P path (Codex + Sonnet + Opus + Haiku + custom)
- Guard collection behind PROFILE_ENV_APPLIED to avoid ?? profiles[0] fallback
Fixes review feedback: standard models are no longer hidden when a
provider profile with custom models is active. Users see both the
standard options and their profile's models.
---------
Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com>
* feat: add NVIDIA NIM and MiniMax provider support
- Add nvidia-nim and minimax to --provider CLI flag
- Add model discovery for NVIDIA NIM (160+ models) and MiniMax
- Update /model picker to show provider-specific models
- Fix provider detection in startup banner
- Update .env.example with new provider options
Supported providers:
- NVIDIA NIM: https://integrate.api.nvidia.com/v1
- MiniMax: https://api.minimax.io/v1
* fix: resolve conflict in StartupScreen (keep NVIDIA/MiniMax + add Codex detection)
* fix: resolve providerProfile conflict (add imports from main, keep NVIDIA/MiniMax)
* fix: revert providerSecrets to match main (NVIDIA/MiniMax handled elsewhere)
* fix: add context window entries for NVIDIA NIM and new MiniMax models
* fix: use GLM-5 as NVIDIA NIM default and MiniMax-M2.5 for consistency
* fix: address remaining review items - add GLM/Kimi context entries, max output tokens, fix .env.example, revert to Nemotron default
* fix: filter NVIDIA NIM picker to chat/instruct models only, set provider-specific API keys from saved profiles
* chore: add more NVIDIA NIM context window entries for popular models
* fix: address remaining non-blocking items - fix base model, clear provider API keys on profile switch
* fix: strip comments before scanning for missing imports
The scanForMissingImports regex matched require() and import() patterns
inside JSDoc comments, causing false-positive missing module detection.
A documented path like `require('./commands/proactive.js')` in a comment
was resolved from the wrong directory, marked as missing, then the global
onResolve handler intercepted ALL imports of that specifier — including
valid ones — replacing them with truthy noop stubs that broke runtime.
Strip block (/* */) and line (//) comments from source before scanning.
* fix: repair 10 pre-existing test failures
- promptIdentity.test.ts: define MACRO global (ISSUES_EXPLAINER etc.)
for test mode where Bun.define build-time replacements aren't active
- context.test.ts: clear OPENAI_MODEL env var in each test — the user's
environment (e.g. OPENAI_MODEL=github_copilot/gpt-5.4) polluted the
provider-qualified lookup, returning wrong context windows
- openclaudePaths.test.ts: set CLAUDE_CONFIG_DIR to force .openclaude
path when ~/.openclaude doesn't exist on the test machine
* feat: add Docker image build and push to GHCR on release
Add Dockerfile (multi-stage build with node:22-slim) and a new docker
job in the release workflow that builds and pushes to ghcr.io when
release-please creates a tag.
* feat(docker): run as non-root user and add smoke test
Run the container as a non-root appuser to reduce blast radius.
Add a smoke test step that runs --version before pushing to GHCR.
* fix(docker): use existing node user instead of creating appuser
Closes#681
* feat: open useful USER_TYPE-gated features to all users
Remove 13 process.env.USER_TYPE === 'ant' gates that restricted useful
features to Anthropic employees. These features work without Anthropic
infrastructure and are now available to all open-build users.
Features opened:
- Agent nesting (sub-agents can spawn sub-agents)
- Effort 'max' persistence in settings
- Plan mode interview phase (controlled by feature flags)
- Sandbox disabled commands (via ~/.claude/feature-flags.json)
- All tips visible to all users (plan mode, feedback, shift-tab)
Simplified:
- Fullscreen defaults to off (use /config to enable)
- Explore agent always uses haiku model
- Plan mode tool uses conservative prompt for all users
Continues the USER_TYPE cleanup from #637 (dead code) and builds
on #639 (local feature flags).
* fix: address Copilot review comments — remove residual dead code
1. bridgeConfig.ts: ungate bridge override functions — return env vars
directly instead of hardcoded undefined
2. bridgeMain.ts + initReplBridge.ts: ungate sessionIngressUrl — read
CLAUDE_BRIDGE_SESSION_INGRESS_URL without USER_TYPE check
3. tools.ts: remove dead ConfigTool/TungstenTool imports, narrow
eslint-disable scope, stub REPLTool/SuggestBackgroundPRTool to null
4. readOnlyValidation.ts: remove orphaned ANT_ONLY_COMMAND_ALLOWLIST
and unused GH_READ_ONLY_COMMANDS import
5. insights.ts: remove entire remote collection plumbing (types,
functions, options, display logic)
6. osc.ts: hardcode supportsTabStatus() to false (internal-only feature)
7. state.ts: simplify addSlowOperation/getSlowOperations to no-ops,
remove dead constants
* fix: address Copilot review on PR #644
1. settings/types.ts: allow 'max' effort level for all users in Zod
schema — was still gated behind USER_TYPE=ant, causing 'max' to be
silently dropped on settings reload
2. shouldUseSandbox.ts: defensively normalize disabledCommands from
feature flag config with Array.isArray() guards
* fix: address second round of Copilot review on PR #644
1. shouldUseSandbox.ts: validate top-level shape of disabledCommands
before accessing properties (handles null/primitive from feature flag)
2. fullscreen.ts: update JSDoc to reflect removal of USER_TYPE default
3. osc.ts: update JSDoc — "Ant-only" → "Currently disabled"
* feat: add Docker image build and push to GHCR on release
Add Dockerfile (multi-stage build with node:22-slim) and a new docker
job in the release workflow that builds and pushes to ghcr.io when
release-please creates a tag.
* feat(docker): run as non-root user and add smoke test
Run the container as a non-root appuser to reduce blast radius.
Add a smoke test step that runs --version before pushing to GHCR.
* fix: resolve 12 bugs across API, MCP, agent tools, web search, and context overflow
API fixes:
- Fix Gemini 400 error: delete 'store: false' field for Gemini endpoints
(was globally injected, Gemini rejects unknown fields)
- Fix session timeout 500 errors after ~25min: add 120s idle timeout
on SSE stream readers in openaiShim and codexShim to detect dead
connections and trigger withRetry reconnection
- Fix context overflow 500 errors: add handler in errors.ts for 500
responses caused by oversized conversation context (too many tokens),
surfacing user-friendly message with recovery actions instead of raw
'API Error: 500'
Agent loop fix:
- Fix premature task completion: detect continuation signals like
'so now I have to do it' in assistant text without tool calls and
inject a meta nudge to force the agent to continue
Web search improvements:
- Increase result counts: Bing/Tavily/Exa/Firecrawl from 10→15,
Mojeek/You/Jina from default→10 (explicit), max_uses 8→15
MCP fixes:
- Reduce default tool timeout from ~27.8 hours to 5 minutes
(tools no longer hang indefinitely on unresponsive servers)
- Add retry logic (3 attempts) for tools/list fetch failures
(prevents all MCP tools from silently disappearing on timeout)
- Add abort signal check in URL elicitation retry loop
- Improve MCP error messages with server and tool name context
Agent tool fixes:
- Fix SendMessage race condition: double-check task status before
auto-resuming stopped agents to prevent duplicate registration
- Fix auto-compact circuit breaker gap: when auto-compact fails 3+
consecutive times, proactively block oversized context BEFORE the
API call instead of letting it 500. Clear message with recovery
instructions (/new, /compact, rewind).
Tests: 850 total, 0 failures (25 new bugfix tests)
* fix: address all 4 review blockers + 6 additional issues from PR #674
Blockers (from Vasanthdev2004 review):
1. Continuation nudge infinite loop — no loop guard
Added continuationNudgeCount to State, capped at MAX_CONTINUATION_NUDGES (3).
Counter increments on each nudge, resets on tool execution (next_turn).
2. Continuation signal regexes too broad — high false-positive rate
Tightened all patterns to require explicit action verbs. Added completion
marker check (done/finished/completed/summary). Broad patterns only fire
on messages <80 chars.
3. BUGFIXES.md in repo root — scope contamination
Removed. PR description already contains this info.
4. AgentTool dump state cleanup is comment-only, not a bug fix
Wrapped clearInvokedSkillsForAgent and clearDumpState in individual
try/catch blocks so one failure doesn't prevent the other.
Additional issues:
5+6. readWithTimeout ignores AbortSignal, timer leak on abort
Added optional signal param to openaiStreamToAnthropic,
codexStreamToAnthropic, collectCodexCompletedResponse, readSseEvents.
Added abort listener that clears idle timer so AbortError surfaces
cleanly instead of spurious idle timeout.
7. MCP error format change breaks consumers
Reverted human-readable message to original errorDetails format.
Moved server/tool context to telemetryMessage param only.
10. AgentTool test broken by comment change
Updated test assertions to match new defensive cleanup text + try/catch.
12. Mojeek test regex dangerously broad
Tightened to match searchParams.set('t', '10') specifically.
14. linkup.ts in providerCounts test — no result count field
Removed from providers list (uses depth param, not result count).
15. Error message overlap between errors.ts and query.ts
Prefixed errorDetails with 'Context overflow (500):' to distinguish.
Tests: 851 pass, 0 fail
---------
Co-authored-by: openclaude-bot <bot@openclaude.ai>
Co-authored-by: Fix Bot <fix@openclaude.dev>
Enable the MESSAGE_ACTIONS feature flag so open-build users get the
shift+up keybinding for the message actions panel.
Gate sites: src/keybindings/defaultBindings.ts, src/screens/REPL.tsx
(5 total). Pure UI/keybinding feature with zero external dependencies.
* feat: local feature flag overrides via ~/.claude/feature-flags.json
Replace the GrowthBook no-op stub with a local JSON file reader that
gives open-build users control over ~50 tengu_* feature flags without
needing Anthropic's GrowthBook server.
How it works:
- On first flag lookup, lazily reads ~/.claude/feature-flags.json
- Returns the configured value if the key exists, defaultValue otherwise
- When the file is absent, behavior is identical to the current stub
- CLAUDE_FEATURE_FLAGS_FILE env var overrides the file path (CI/testing)
Example ~/.claude/feature-flags.json:
{ "tengu_kairos_cron": true, "tengu_scratch": true }
Continues the infrastructure work from #315 and #352. This is a
prerequisite for replacing remaining USER_TYPE gates with local config.
* fix: use ESM imports and validate JSON shape in growthbook stub
- Replace require('fs'/'path'/'os') with ESM imports (node: prefix)
to avoid ReferenceError in ESM bundle output
- Validate JSON.parse result is a plain object before using `in` operator
to prevent TypeError on non-object JSON values
Addresses Copilot review comments on #639
* fix: reset flags cache in resetGrowthBook and refreshGrowthBookFeatures
Set _flags back to undefined so subsequent lookups re-read the JSON
file. Enables runtime reload and proper test isolation.
Addresses Copilot review comment on #639
* docs: explain why checkSecurityRestrictionGate is excluded from local flags
This is a remote killswitch for bypassPermissions mode — exposing it
via the local JSON file would let users accidentally disable
--dangerously-skip-permissions without understanding why.
* test: add unit tests for growthbook stub local feature flags
Covers: valid JSON loading, missing file fallback, malformed JSON,
non-object JSON (primitive, array), cache invalidation via
resetGrowthBook/refreshGrowthBookFeatures, all getter variants,
and checkSecurityRestrictionGate always returning false.
12 tests, 21 assertions.
* fix: use Object.hasOwn instead of in operator for flag lookup
Prevents inherited prototype properties (toString, constructor, etc.)
from being returned as flag values.
Addresses Copilot review comment on #639
* fix: align gate stub signatures and add Boolean coercion
Address remaining Copilot review feedback:
- checkSecurityRestrictionGate: accept gate param to match real signature
- checkStatsigFeatureGate/checkGate: coerce with Boolean() like real impl
* feat: implement Monitor tool for streaming shell output
Add the Monitor tool that executes shell commands in the background and
streams stdout line-by-line as notifications to the model. This enables
real-time monitoring of logs, builds, and long-running processes.
Implementation:
- MonitorTool (src/tools/MonitorTool/) — spawns LocalShellTask with
kind='monitor', returns immediately with task ID
- MonitorMcpTask (src/tasks/MonitorMcpTask/) — task lifecycle management
and agent cleanup via killMonitorMcpTasksForAgent()
- MonitorPermissionRequest — permission dialog component
The codebase already had all integration points wired (tools.ts, tasks.ts,
PermissionRequest.tsx, LocalShellTask kind='monitor', BashTool prompt).
This PR provides the missing implementations.
* fix: command-specific permission rule + architecture docs
- MonitorPermissionRequest: "don't ask again" now creates a
command-prefix rule (like BashTool) instead of a blanket
tool-name-only rule that would auto-allow all Monitor commands
- MonitorMcpTask: clarify architecture comments explaining why
monitor_mcp type exists as a registry stub while actual tasks
are local_bash with kind='monitor'
* fix: address Copilot review feedback
- Fix permission rule field: expression → ruleContent (Copilot #1)
- Handle empty command prefix: skip rule creation (Copilot #2)
- Remove unused useTheme() import (Copilot #3)
- Save permission rules under 'Bash' toolName so bashToolHasPermission
can match them — Monitor delegates to Bash permission system (Copilot #4)
- Remove unused logError import from MonitorMcpTask (Copilot #6)
- Copilot #5 (getAppState throws): same pattern as BashTool:915, not a bug
* feat: activate local-only team memory in open build
Enable the TEAMMEM feature flag and the isTeamMemoryEnabled() gate so
team memory works in local-only mode for all open-build users.
Team memory is a shared memory system scoped per-project, stored at
~/.claude/projects/<project>/memory/team/. The implementation is
already almost entirely local — extraction, UI, prompts, file
detection, and path validation all work on local files.
The cloud sync overlay (OAuth + API) is cleanly separated: the
watcher does an early return when OAuth is unavailable, so the
feature degrades gracefully to local-only storage with no crashes.
What works locally:
- Memory extraction (auto + team, combined prompts)
- Team MEMORY.md loaded into conversation context
- File selector with team memory folder option
- Collapse tracking (read/search/write counts)
- Secret scanning before persistence
- Path validation + symlink protection
What requires OAuth (not available in open build):
- Cloud sync between team members
- Automatic push/pull via file watcher
* fix: preserve opt-out gate for team memory via feature flag
Change isTeamMemoryEnabled() to read tengu_herring_clock with default
true instead of unconditional return true. This enables team memory by
default while preserving user opt-out via ~/.claude/feature-flags.json.
* feat: activate coordinator mode in open build
Enable the COORDINATOR_MODE feature flag and create the missing
src/coordinator/workerAgent.ts module that provides worker agent
definitions for the coordinator.
Coordinator mode is a multi-agent system where a coordinator agent
orchestrates independent workers via AgentTool, SendMessageTool,
and TaskStopTool. The implementation was already 99% complete
(19KB coordinatorMode.ts, 26 gate sites across 15 files) — only
the workerAgent module was missing from the source snapshot.
Workers get the standard built-in agents (general-purpose, explore,
plan). The coordinator system prompt (252 lines) handles all
orchestration logic.
Activate at runtime: CLAUDE_CODE_COORDINATOR_MODE=1
Optional scratchpad: set {"tengu_scratch": true} in
~/.claude/feature-flags.json (#639)
* fix: add worker agent type for coordinator mode
The coordinator system prompt instructs the model to spawn workers with
subagent_type: "worker", but no agent had agentType === 'worker'.
This caused AgentTool to throw "Agent type 'worker' not found" on
every coordinator spawn attempt.
Add a WORKER_AGENT definition that spreads GENERAL_PURPOSE_AGENT with
agentType: 'worker'. Also use the narrower BuiltInAgentDefinition type.
* feat: activate built-in explore and plan agents in open build
Enable BUILTIN_EXPLORE_PLAN_AGENTS so Explore (fast, haiku, read-only)
and Plan (architect, read-only) agents are available to all users in
both normal and coordinator modes.
This resolves the inconsistency flagged in code review: coordinator
workers had access to Explore/Plan agents while normal sessions did not.
The GrowthBook A/B test gate (tengu_amber_stoat) defaults to true via
the no-telemetry stub. Users can disable via feature-flags.json (#639).
* fix: replace broken bun:bundle shim with source pre-processing
The `onResolve`/`onLoad` plugin shim for `bun:bundle` was silently
ineffective in Bun v1.3.9+ — the `bun:` namespace is resolved by
Bun's native C++ resolver before the JS plugin phase runs. This meant
ALL `feature()` flags evaluated to `false` regardless of the
`featureFlags` map in build.ts (including `MONITOR_TOOL: true`).
Replace the shim with a source pre-processing step that:
1. Strips `import { feature } from 'bun:bundle'` from .ts/.tsx files
2. Replaces `feature('FLAG')` calls with boolean literals
3. Restores original files in a `finally` block after Bun.build()
Also extend the missing-module scanner to detect `require()` and
dynamic `import()` calls — not just static `import ... from` — since
modules behind feature() gates become resolvable when flags are enabled.
* fix: ensure source files are always restored after build
- Add SIGINT/SIGTERM handlers to restore pre-processed source files
on abrupt termination (Ctrl+C, kill)
- Replace process.exit(1) with process.exitCode = 1 so the finally
block runs on build failure
The provider profile activation guard in applyActiveProviderProfileFromConfig()
only checked CLAUDE_CODE_USE_* environment flags, which are never set for the
default anthropic provider. This allowed two terminals sharing ~/.claude.json
to overwrite each other's active provider when one was using anthropic and
the other a third-party provider.
Now also checks the OCODE_PROVIDER_PROFILE_APPLIED flag, which is set by all
profiles including anthropic, preventing cross-terminal interference.
Co-authored-by: Ali Alakbarli <ali.alakbarli@users.noreply.github.com>
* feat: add allowBypassPermissionsMode setting
Allow bypass permissions mode to appear in the mode list via
settings.json without requiring the --allow-dangerously-skip-permissions
CLI flag. The disableBypassPermissionsMode setting retains priority.
* fix: address Copilot review feedback on allowBypassPermissionsMode
- Security: read allowBypassPermissionsMode only from trusted settings
sources (user/local/flag/policy), excluding projectSettings to prevent
a malicious repo from enabling bypass mode
- UX: update error messages to reference the correct CLI flag
(--allow-dangerously-skip-permissions) and the new settings option
- Tests: add schema validation tests for the new field
* feat: enhance codex provider resolution with shortcut aliases and improved base URL handling
* fix: enhance codex alias resolution to include shell model
* feat: enhance Codex provider resolution to support new aliases and base URL handling
* fix: update base URL resolution logic for Codex models in GitHub mode
* fix: update provider transport logic to enforce Codex responses and adjust base URL handling
* fix: update provider request resolution to respect custom base URLs and adjust transport logic
* fix: restore OPENAI_MODEL environment variable handling in tests and provider config
* feat: implement /loop command with fixed and dynamic scheduling modes
Enable cron tools and /loop skill without the AGENT_TRIGGERS build flag
by removing feature guards from tools.ts, REPL.tsx, and skill registration.
The isKairosCronEnabled() runtime gate now enables cron unconditionally for
open builds while preserving the GrowthBook kill switch for ant builds.
The /loop skill supports four modes: fixed-interval with prompt, fixed-interval
maintenance, dynamic-prompt (self-pacing), and dynamic maintenance (bare /loop).
* chore: remove unused DEFAULT_INTERVAL constant from loop skill
* revert: drop infra changes, scope PR to /loop skill rewrite only
The cron activation layer (AGENT_TRIGGERS guard removal, isKairosCronEnabled
hardcode) is covered by an in-flight stack (#633, #639). Scope this PR to
just the loop.ts rewrite and its tests so it can land cleanly on top.
* fix: restore infra changes needed for /loop in open build
Bun's constant folder evaluates feature('AGENT_TRIGGERS') at bundle time
through the bun:bundle shim — even when the flag is flipped to true in
build.ts, the folded value is cached from the previous build and stays false.
This means the feature-gated require() blocks for cron tools, useScheduledTasks,
and loop skill registration all compile to dead code regardless of the flag.
Fix by removing the AGENT_TRIGGERS guards from the specific paths /loop needs:
- tools.ts: cron tools always registered (isEnabled gates visibility)
- REPL.tsx: useScheduledTasks always mounted
- index.ts: registerLoopSkill via static import, called unconditionally
- prompt.ts: isKairosCronEnabled() bypasses feature flag for non-ant builds
* fix: replace backslash line continuations with explicit delimiters in loop prompts
The backslash-newline sequences inside template literals were acting as
line continuations, collapsing newlines and merging prompt content with
surrounding instruction text. Replace with --- BEGIN/END --- markers
for unambiguous delimiting.
Also add tests for trailing "every" clause parsing, human-readable unit
normalization, and the non-interval "check every PR" case.
* fix: remove remaining AGENT_TRIGGERS guards from print.ts and constants/tools.ts
Completes the cron guard removal started in the previous commit.
The cron scheduler in non-interactive (-p) mode was dead because
print.ts still gated cronSchedulerModule/cronGate requires behind
feature('AGENT_TRIGGERS'), which Bun constant-folds to false in open
builds. Similarly, cron tool names were absent from
IN_PROCESS_TEAMMATE_ALLOWED_TOOLS.
Remove all three guards so the scheduler initialises (gated at runtime
by isKairosCronEnabled) and cron tools are allowed for in-process
teammates in all builds.
- Raise context window fallback from 8k to 128k for unknown OpenAI-compat models.
The 8k fallback caused effective context (8k minus output reservation) to go
negative, making auto-compact fire on every single message.
- Add safety floor in getEffectiveContextWindowSize(): effective context is
always at least reservedTokensForSummary + 13k buffer, ensuring the
auto-compact threshold stays positive.
- Add missing MiniMax model entries (M2.5, M2.5-highspeed, M2.1, M2.1-highspeed)
all at 204,800 context / 131,072 max output per MiniMax docs.
- Add tests for MiniMax variants, 128k fallback, and autoCompact floor.
Fixes#635
Co-authored-by: root <root@vm7508.lumadock.com>
The OPENAI_CONTEXT_WINDOWS/OPENAI_MAX_OUTPUT_TOKENS tables only contained
the `github:copilot:<model>` namespaced form used when talking directly to
Copilot via /onboard-github. When OpenClaude is pointed at a LiteLLM proxy
(which routes Copilot using the standard `github_copilot/<model>` convention),
the lookup missed and fell back to the conservative 8k default — causing the
compaction loop to fire repeatedly on every tick and blocking requests
before they left the client with repeated "not in context window table"
warnings on stderr.
Mirror the 11 active Copilot models with LiteLLM-style keys in both tables.
No behavior change for users of /onboard-github since namespaced entries
remain untouched and `lookupByKey` picks exact matches first.
The previous `isPrivateHostname` used a list of regexes against
`URL.hostname`. Several literal-address forms slipped past it:
- IPv4-mapped IPv6 `[::ffff:127.0.0.1]` (WHATWG URL normalizes to
`[::ffff:7f00:1]`, which no regex matched) — lets callers reach
loopback and other private v4 via an IPv6 literal.
- ULA `fc00::/7` (e.g. `[fc00::1]`) — not covered.
- Link-local `fe80::/10` (e.g. `[fe80::1]`) — not covered.
- IPv4 `169.254.0.0/16` (cloud metadata, including 169.254.169.254),
`100.64.0.0/10` (CGNAT), and the full `0.0.0.0/8` — not covered.
- The IPv6 regex `/^\[::1?\]$/` also required brackets, but `URL.hostname`
returns bracketed form anyway, so this part happened to work.
WHATWG `new URL(...)` already normalizes short-form / numeric / hex /
octal IPv4 to dotted-quad before we see it, so those cases were in fact
handled — the remaining gaps were IPv6 and a few missing v4 ranges.
Replace the regex list with:
- a dotted-quad IPv4 parser + int range check covering 0/8, 10/8,
100.64/10, 127/8, 169.254/16, 172.16/12, 192.168/16;
- a small IPv6 parser (handles `::` compression and embedded v4 suffix)
+ a byte-range check covering `::`, `::1`, IPv4-mapped (recursing
into the v4 classifier), IPv4-compatible, `fc00::/7`, `fe80::/10`,
and `fec0::/10`.
Export `isPrivateHostname` and add unit tests covering every bypass
listed above plus public-address negatives.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
`fetchWithRetry` created a fresh `AbortController` per attempt and did:
signal?.addEventListener('abort', () => controller.abort(), { once: true })
The listener was never removed. Consequences:
- On retry, a second listener was attached to the caller's signal,
each closing over a different controller.
- After a successful fetch, the listener remained on the caller's
signal indefinitely, referencing a controller whose work was done.
For a long-lived caller signal this is a slow leak.
- The `{ once: true }` only helps if the signal actually fires — on
non-aborted signals the listener stays attached forever.
Replace the manual controller + timer + listener dance with
`AbortSignal.any([signal, AbortSignal.timeout(ms)])`, which the
codebase already uses elsewhere (see src/services/mcp/xaa.ts). This:
- has no user-code listener to leak,
- gives each attempt a fresh independent timeout,
- cleanly distinguishes caller-initiated abort from timeout via
`signal.aborted` vs `timeoutSignal.aborted` before rewriting the
error as "Custom search timed out after Ns".
Also resets `lastStatus` per attempt so a 5xx on attempt 0 can't leak
into attempt 1's retry decision, and collapses the two redundant
retry branches (`lastStatus >= 500` and `lastStatus === undefined`)
into one.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
React 19's react-reconciler@0.33 mutation path calls commitUpdate with
(instance, type, oldProps, newProps, fiber), but our Ink host config
still expected an updatePayload from prepareUpdate. That left mounted
ink-* nodes with stale onKeyDown, tabIndex, and textStyles, making menu
navigation and highlights appear stuck until remount.
Diff old/new props directly inside commitUpdate and add regression tests
covering in-place updates for ink-box handlers/attributes and ink-text
styles.
* fix: report cache reads in streaming and correct cost calculation
Fix two bugs in how the OpenAI-to-Anthropic shim handles cached tokens:
1. codexShim: streaming message_delta missing cache_read_input_tokens
The codexStreamToAnthropic() function builds the final message_delta
usage object inline (not through makeUsage()), and only included
input_tokens and output_tokens. cache_read_input_tokens was always 0,
so /cost never showed cache reads for Responses API models (GPT-5+).
Also fix makeUsage() to read input_tokens_details.cached_tokens and
prompt_tokens_details.cached_tokens for the non-streaming path.
2. Both shims: cost double-counting from convention mismatch
OpenAI includes cached tokens in input_tokens/prompt_tokens (i.e.,
input_tokens = uncached + cached). Anthropic treats input_tokens as
uncached only. The cost formula was:
cost = input_tokens * inputRate + cache_read * cacheRate
This double-counts cached tokens. Fix by subtracting cached from
input during the conversion:
input_tokens = prompt_tokens - cached_tokens
In practice this was inflating reported costs by ~2x for sessions
with high cache hit rates (which is most sessions, since Copilot
auto-caches server-side).
Fixes#515
* fix: omit zero cache read/write fields from /cost output
Only show "cache read" and "cache write" in /cost per-model usage when
the value is > 0. Providers like GitHub Copilot never report
cache_creation_input_tokens (the server manages its own cache), so
showing "0 cache write" on every line is misleading — it implies caching
is not working when it actually is.
Before:
claude-haiku: 2.6k input, 151 output, 39.8k cache read, 0 cache write ($0.04)
After:
claude-haiku: 2.6k input, 151 output, 39.8k cache read ($0.04)
---------
Co-authored-by: Zartris <14197299+Zartris@users.noreply.github.com>
Set store: false in the request body for both the Chat Completions path
and the /responses fallback path in openaiShim.ts.
The codexShim (Responses API primary path) already sets store: false.
The Chat Completions path and the /responses fallback in openaiShim were
missing it.
store: false tells the API provider not to persist conversation data for
model training, logging, or other non-operational purposes. This is a
privacy measure — it does not affect caching or functionality.
Note: Whether third-party proxies (e.g. GitHub Copilot) honour this
parameter is provider-dependent, but setting it is a reasonable default
for user privacy.
Co-authored-by: Zartris <14197299+Zartris@users.noreply.github.com>
Add context_window and max_output_tokens entries for all models available
through the GitHub Copilot proxy (Claude, GPT, Gemini, Grok), sourced from
https://api.githubcopilot.com/models.
Models are namespaced as "github:copilot:<model>" to avoid collisions with
the same model names served by other providers (which may have different
limits). A new lookupByKey() helper and qualified-key lookup in
lookupByModel() ensures the correct limits are selected when
OPENAI_MODEL=github:copilot.
Without this, Claude models on Copilot would use default context/output
limits that may not match the proxy's actual constraints, causing 400 errors
like "max_tokens is too large".
Related: #515
Co-authored-by: Zartris <14197299+Zartris@users.noreply.github.com>
Treat profile-managed env as restart state rather than explicit user intent so saved OpenAI-compatible profiles can replace stale Ollama values on startup and persist correctly across restarts.
Co-authored-by: Claude Opus 4.6 <noreply@openclaude.dev>
* Stop canonical Anthropic headers from leaking into 3P shim requests
The remaining blocker from PR #268 was that canonical Anthropic headers such as
`anthropic-version` and `anthropic-beta` could still ride through supported 3P
paths even after the earlier x-anthropic/x-claude scrubber work. This tightens
header filtering inside the shim itself so direct defaultHeaders, env-driven
client setup, providerOverride routing, and per-request header injection all
share the same scrubber.
Constraint: Preserve non-Anthropic custom headers and provider auth while stripping only Anthropic/OpenClaude-internal headers from 3P requests
Rejected: Rely on client.ts filtering alone | direct shim construction and per-request headers would still leave gaps
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep header scrubbing centralized in the shim so new call paths do not reopen 3P leakage bugs
Tested: bun test src/services/api/openaiShim.test.ts src/services/api/client.test.ts src/utils/context.test.ts
Tested: bun run test:provider
Tested: bun run build && node dist/cli.mjs --version
Not-tested: bun run typecheck (repository baseline currently fails in many unrelated files)
* Keep OpenAI client tests from restoring undefined env as strings
The new header-leak regression tests in client.test.ts restored environment
variables via direct assignment, which can leave literal "undefined" strings in
process.env when the original value was unset. This switches the teardown over
to the same restore helper pattern already used in openaiShim.test.ts.
Constraint: Keep the fix limited to test hygiene without altering runtime behavior
Rejected: Restore only the two env vars Copilot called out | using one helper for all test env restores is simpler and less error-prone
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Use restore helpers for env teardown in tests so unset values stay deleted instead of becoming the string "undefined"
Tested: bun test src/services/api/client.test.ts src/services/api/openaiShim.test.ts src/utils/context.test.ts
Not-tested: Full provider suite (unchanged runtime path)
* Prevent GitHub Codex requests from forwarding unsanitized Anthropic headers
A base-sync with upstream exposed a separate GitHub+Codex transport branch
that still merged per-request headers raw before adding Copilot headers.
This keeps the filter aligned across Codex-family paths and adds explicit
regression tests for GitHub Codex routing, including providerOverride.
Constraint: Must not push or modify GitHub state while validating the reviewer concern
Rejected: Leave the GitHub Codex path unchanged | runtime repro showed anthropic-* headers still leaked after the upstream sync
Confidence: high
Scope-risk: narrow
Directive: Keep header scrubbing consistent across every Codex-family transport branch when provider routing changes
Tested: bun test src/services/api/openaiShim.test.ts
Tested: bun test src/services/api/client.test.ts src/services/api/codexShim.test.ts src/services/api/providerConfig.github.test.ts
Tested: bun run build
Not-tested: Full repository test suite
Treat default select focus as initial state so /theme and first-run previews follow keyboard navigation again.
Co-authored-by: anandh8x <test@example.com>
Add a /cache-probe slash command for debugging prompt caching behaviour
on OpenAI-compatible providers (GitHub Copilot, OpenAI direct).
The command sends two identical API requests in sequence and compares the
raw server response usage stats, showing:
- Input/output token counts
- Cache read tokens (from prompt_tokens_details or input_tokens_details)
- Latency for each request
- Cache hit rate percentage
Usage:
/cache-probe # test default model
/cache-probe claude-sonnet-4 # test specific model
/cache-probe gpt-5.4 --no-key # test without prompt_cache_key
The --no-key flag omits prompt_cache_key/prompt_cache_retention/store to
test whether the server does content-based auto-caching (it does on
GitHub Copilot).
This is a debugging/diagnostic tool, not intended for regular use. It was
instrumental in discovering that:
1. Copilot auto-caches server-side based on content hash
2. prompt_cache_key is ignored by the proxy
3. The streaming path was not reporting cached tokens
Only enabled when the provider is OpenAI or GitHub (not for firstParty
Anthropic which has different caching semantics).
Related: #515
Co-authored-by: Zartris <14197299+Zartris@users.noreply.github.com>
* feat: add AutoFix config schema and reader module
Implements AutoFixConfigSchema (Zod v4) with validation for lint/test
commands, maxRetries (0-10, default 3), and timeout (1000-300000ms,
default 30000). Adds getAutoFixConfig helper that returns null for
disabled or invalid configs. All 9 unit tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add autoFix runner with lint/test command execution
Implements AutoFixRunner (Task 2) - executes lint and test shell commands
sequentially, short-circuits on lint failure, handles timeouts, and
produces structured AutoFixResult with AI-friendly error summaries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add autoFix field to SettingsSchema with integration tests
Integrates AutoFixConfigSchema into SettingsSchema so autoFix settings
are validated at the settings layer. Adds two integration tests verifying
that valid configs are accepted and invalid configs (enabled with no
commands) are rejected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add autoFix hook integration helpers (Task 4)
Implements shouldRunAutoFix and buildAutoFixContext functions used by
the PostToolUse hook to determine when to run auto-fix and format
errors as AI-readable context for injection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: wire autoFix into PostToolUse hook flow (Task 5)
Add auto-fix lint/test check after existing PostToolUse hooks in
runPostToolUseHooks. When autoFix is configured in settings, runs
lint/test commands after file_edit/file_write tools and yields
errors as hook_additional_context for the model to act on.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add /auto-fix slash command
Adds the /auto-fix prompt command that helps users configure autoFix settings
(lint/test commands, maxRetries, timeout) in .claude/settings.json.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove unused imports in autoFixRunner test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review feedback — enforce maxRetries, wire abort signal, use cross-platform shell
1. Enforce maxRetries: track auto-fix attempts per query chain in toolHooks.ts
and stop feeding errors back after the configured limit is reached.
2. Wire abort signal to subprocess: subscribe to AbortController signal in
runCommand() and kill the process tree on abort. Uses detached process
groups on Unix to ensure child processes are also terminated.
3. Replace hardcoded bash with shell:true: use Node's cross-platform shell
resolution instead of spawn('bash', ['-c', ...]) so auto-fix commands
work on Windows and non-bash environments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: custom web search — WEB_URL_TEMPLATE not recognized, timeout too short, silent native fallback
1. custom.ts: Add WEB_URL_TEMPLATE to isConfigured() so the custom provider
is recognized when configured via URL template alone.
2. custom.ts: Bump DEFAULT_TIMEOUT_SECONDS from 15s to 120s.
Self-hosted search APIs (SearXNG, internal) commonly need 30-90s.
3. WebSearchTool.ts: When an explicit adapter is selected via
WEB_SEARCH_PROVIDER=custom, do not silently fall through to the
native Anthropic path on adapter errors or 0-hit results.
- 0 hits: return directly (no fallback)
- Error: throw the real error (no fallback)
- Auto mode: existing fallback behavior preserved
* fix: tighten auto-mode adapter fallback — only swallow transient errors
Address review feedback: in auto mode, only fall through to native on
transient errors (network failure, timeout, HTTP 5xx). Config and
guardrail errors (SSRF, HTTPS, bad URL, header allowlist, etc.) now
surface properly instead of being silently swallowed.
---------
Co-authored-by: FluxLuFFy <fluxluffy@users.noreply.github.com>
* refactor: provider adapter system + 7 new search providers
Architecture:
- Each search backend is a small adapter implementing SearchProvider
- 12 providers: custom, tavily, exa, you, jina, bing, mojeek, linkup, firecrawl, duckduckgo + native
- WEB_SEARCH_PROVIDER controls selection: auto (fallback chain) or specific provider
- Auth always in headers, never in query strings
Bug fixes from review feedback:
- Fix applyDomainFilters catch block: keep hits with malformed URLs on blocked_domains
(can't confirm blocked), drop on allowed_domains (can't confirm allowed)
- Add safeHostname() helper: safely extract hostname from URLs without throwing
- Replace unsafe new URL(r.url).hostname in 7 providers with safeHostname()
- Remove dead code: buildAllHeaders, buildAuthHeaders, parseExtraHeaders from types.ts
- Fix WEB_PARMS typo: consistently use WEB_QUERY_PARAM everywhere
- AbortSignal forwarded to fetch() in all 12 providers
- DuckDuckGo: wrap dynamic import in try/catch for graceful error
- Exa: remove double domain filtering (server-side already)
- runSearch(): aggregate all provider errors instead of throwing only the last one
- Retry logic: check numeric status code directly, retry 5xx/network, skip 4xx
Test coverage (44 tests, all passing):
- types.test.ts: safeHostname, normalizeHit, applyDomainFilters (20 tests)
- index.test.ts: getProviderMode, getProviderChain, getAvailableProviders (13 tests)
- custom.test.ts: extractHits flexible response parsing (11 tests)
Co-authored-by: FluxLuFFy <195792511+FluxLuFFy@users.noreply.github.com>
* security: add guardrails to custom search provider (Option B)
- HTTPS-only by default (opt-out: WEB_CUSTOM_ALLOW_HTTP=true)
- Private/localhost IPs blocked by default (opt-out: WEB_CUSTOM_ALLOW_PRIVATE=true)
- Header allowlist: only known-safe headers allowed unless WEB_CUSTOM_ALLOW_ARBITRARY_HEADERS=true
- Configurable timeout in seconds (WEB_CUSTOM_TIMEOUT_SEC, default 15)
- Configurable POST body limit (WEB_CUSTOM_MAX_BODY_KB, default 300)
- Removed max URL size restriction
- Audit log warning on first custom search call
- Updated .env.example and README_SEARCH_PROVIDERS.md with all new options
* fix: remove custom provider from auto chain (Option 1)
Remove customProvider from the auto fallback chain so it is only
available when WEB_SEARCH_PROVIDER=custom is explicitly selected.
Changes:
- Remove customProvider from ALL_PROVIDERS array in providers/index.ts
- Add 3 new tests verifying custom is excluded from auto chain
- Update README_SEARCH_PROVIDERS.md: auto priority, mode table, note
- Update .env.example: auto priority comment, custom mode annotation
All 47 tests pass (44 existing + 3 new).
Co-Authored-By: @Vasanthdev2004
* fix: address review blockers (routing, abort, config check, domain matching)
1. Native/Codex routing precedence in auto mode
shouldUseAdapterProvider() now checks if native/first-party/vertex/foundry
or Codex paths are available before falling back to adapter providers.
Auto mode: native paths take precedence; adapter is fallback only.
2. AbortError stops provider chain immediately
runSearch() now checks for AbortError/aborted signal before continuing
the fallback chain. Cancelled searches don't create extra outbound requests.
3. Explicit provider mode fails fast on missing credentials
runSearch() validates isConfigured() for explicit modes before attempting
requests. Throws clear error: 'Search provider "X" is not configured.'
4. Domain filter exact-or-subdomain matching (fixes suffix collision)
New hostMatchesDomain() helper: exact match or .subdomain match.
badexample.com no longer matches example.com.
5. Tests: 56 pass (9 new) covering all 4 fixes
Co-Authored-By: @Vasanthdev2004
---------
Co-authored-by: Claude Fix <fix@openclaude.local>
Co-authored-by: FluxLuFFy <195792511+FluxLuFFy@users.noreply.github.com>
Co-authored-by: bot <bot@openclaw.ai>
Root cause: IMAGE_MAX_WIDTH and IMAGE_MAX_HEIGHT were set to 2000 — exactly the APIs many-image dimension limit. Images resized to exactly 2000px would get rejected when the conversation accumulated enough images to trigger the API's many-image mode.
Fix: Changed both constants from 2000 to 1568 in src/constants/apiLimits.ts:42-43. This is the resolution the API internally downscales to anyway (documented in the API's encoding/full_encoding.py), so there is zero effective quality loss. All images are now safely below the many-image threshold.
export const IMAGE_MAX_WIDTH = 1568
export const IMAGE_MAX_HEIGHT = 1568
Impact: The single constant change propagates everywhere — imageResizer.ts uses IMAGE_MAX_WIDTH/IMAGE_MAX_HEIGHT for all resize decisions, and the error messages reference these constants dynamically. No other files need changes.
The select cursor highlight was broken because isDeepStrictEqual in
use-select-navigation.ts and use-multi-select-state.ts would fail when
options contained identity-unstable properties (JSX label elements,
function onChange callbacks, computed disabled booleans). This caused
the reset logic to fire on every re-render, resetting focusedValue
back to the first option.
Replace isDeepStrictEqual with optionsNavigateEqual which only compares
properties that affect navigation behavior: value, disabled, and type.
ReactNode labels and function callbacks are intentionally excluded as
they are identity-unstable but don't change navigation semantics.
Fixes#472
Co-authored-by: OpenClaude Worker 3 <worker-3@openclaude.local>
Fixes#430. In normalizeSchemaForOpenAI(), the strict branch was adding every
property key to required[], including optional ones. This caused providers like
Groq, Azure OpenAI, and others to reject valid tool calls with a 400 /
tool_use_failed error because the model correctly omits optional arguments but
the provider sees them as missing required fields.
Root cause: the strict branch used `[...existingRequired, ...allKeys]` instead
of `existingRequired.filter(k => k in normalizedProps)`. The Gemini branch
already had the correct logic.
Fix: align the strict branch with the Gemini branch — only keep properties that
were already marked required in the original schema. The additionalProperties:
false constraint is preserved as strict-mode providers still require it.
Add regression test covering the Read tool schema (file_path required,
offset/limit/pages optional).
* fix: defer startup plugin checks and suppress recommendation dialogs during startup window (issue #363)
Root cause: performStartupChecks() fires immediately on REPL mount,
triggering plugin loading which populates trackedFiles, which triggers
useLspPluginRecommendation to surface an LSP recommendation dialog.
Since promptTypingSuppressionActive is false before any user input,
getFocusedInputDialog() returns the dialog, unmounting PromptInput
entirely and making the CLI appear frozen.
Fix: Two-pronged approach:
1. Defer performStartupChecks by 1500ms and gate on
promptTypingSuppressionActive so startup checks dont run while
the user is typing or has early input buffered
2. Suppress lower-priority startup dialogs (LSP recommendation,
plugin hint, desktop upsell) until startupChecksStartedRef is
true, preventing them from stealing focus during the vulnerable
startup window
This also explains why --bare mode and disabling plugins work:
--bare mode skips plugin loading entirely, and disabling the
autoresearch plugin eliminates the LSP match, so lspRecommendation
stays null and PromptInput renders normally.
* fix: move startup checks effect after promptTypingSuppressionActive declaration
Fixes temporal dead zone warning flagged by code-quality bot.
promptTypingSuppressionActive is declared on line ~1340 but the
useEffect was on line ~800, causing a reference-before-declaration.
Also adds missing semicolons for style consistency.
* fix: gate startup checks on prompt readiness, not just a timeout (issue #363)
The previous approach used a fixed 1500ms timeout, but as gnanam1990
pointed out, if a user pauses for >1.5s before typing the timer can
still fire and recommendation dialogs can steal focus. This is a
timing mitigation, not a reliable fix.
New approach: gate startup checks on actual prompt readiness:
1. After first message submission (submitCount > 0) — always safe
2. After grace period (3s) elapsed AND user is idle — safe because
no dialog will interrupt an idle user who hasn't started typing
3. While user is actively typing — deferrred until they stop
This ensures startup checks never steal focus from a prompt the user
is about to type into, regardless of how long they pause before typing.
Also removes the old STARTUP_CHECK_DELAY_MS constant in favor of
STARTUP_GRACE_PERIOD_MS with clearer semantics.
* fix: move startup checks after submitCount declaration to avoid temporal dead zone
Code quality bot flagged that submitCount was used before its declaration.
Moved the entire startup checks block to after the submitCount useState
declaration. Also added nullish coalescing (submitCount ?? 0) per bot
suggestion.
* fix: gate startup checks strictly on first submission, remove grace period (issue #363)
As gnanam1990 pointed out, the 3s grace period still allows the failure
mode: if a user pauses for a few seconds before typing, startup checks
fire and recommendation dialogs steal focus. A grace period is still a
timing mitigation, not a reliable fix.
New approach: startup checks only run after the user has submitted their
first message (submitCount > 0). No grace period, no timeout. This
guarantees the prompt gets first interaction — no dialog can steal focus
before the user has actually used the CLI.
If the user never submits a message, startup checks never run. That's
acceptable because with no user interaction there's no need for plugin
installations or marketplace seeding.
---------
Co-authored-by: OpenClaude Worker 3 <worker-3@openclaude.local>
* update gitHub copilot API with offical client id and update model configurations
* test: add unit tests for exchangeForCopilotToken and enhance GitHub model normalization
* remove PAT token feature
* test(api): harden provider tests against env leakage
* Added back trimmed github auth token
* added auto refresh logic for auto token along with test
* fix: remove forked provider validation in cli.tsx and clear stale provider env vars in /onboard-github
* refactor: streamline environment variable handling in mergeUserSettingsEnv
* fix: clear stale provider env vars to ensure correct GH routing
* Remove internal-only tooling from the external build (#352)
* Remove internal-only tooling without changing external runtime contracts
This trims the lowest-risk internal-only surfaces first: deleted internal
modules are replaced by build-time no-op stubs, the bundled stuck skill is
removed, and the insights S3 upload path now stays local-only. The privacy
verifier is expanded and the remaining bundled internal Slack/Artifactory
strings are neutralized without broad repo-wide renames.
Constraint: Keep the first PR deletion-heavy and avoid mass rewrites of USER_TYPE, tengu, or claude_code identifiers
Rejected: One-shot DMCA cleanup branch | too much semantic risk for a first PR
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Treat full-repo typecheck as a baseline issue on this upstream snapshot; do not claim this commit introduced the existing non-Phase-A errors without isolating them first
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Not-tested: Full repo typecheck (currently fails on widespread pre-existing upstream errors outside this change set)
* Keep minimal source shims so CI can import Phase A cleanup paths
The first PR removed internal-only source files entirely, but CI provider
and context tests import those modules directly from source rather than
through the build-time no-telemetry stubs. This restores tiny no-op source
shims so tests and local source imports resolve while preserving the same
external runtime behavior.
Constraint: GitHub Actions runs source-level tests in addition to bundled build/privacy checks
Rejected: Revert the entire deletion pass | unnecessary once the import contract is satisfied by small shims
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: For later cleanup phases, treat build-time stubs and source-test imports as separate compatibility surfaces
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (still noisy on this upstream snapshot)
---------
Co-authored-by: anandh8x <test@example.com>
* Reduce internal-only labeling noise in source comments (#355)
This pass rewrites comment-only ANT-ONLY markers to neutral internal-only
language across the source tree without changing runtime strings, flags,
commands, or protocol identifiers. The goal is to lower obvious internal
prose leakage while keeping the diff mechanically safe and easy to review.
Constraint: Phase B is limited to comments/prose only; runtime strings and user-facing labels remain deferred
Rejected: Broad search-and-replace across strings and command descriptions | too risky for a prose-only pass
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Remaining ANT-ONLY hits are mostly runtime/user-facing strings and should be handled separately from comment cleanup
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
* Neutralize internal Anthropic prose in explanatory comments (#357)
This is a small prose-only follow-up that rewrites clearly internal or
explanatory Anthropic comment language to neutral wording in a handful of
high-confidence files. It avoids runtime strings, flags, command labels,
protocol identifiers, and provider-facing references.
Constraint: Keep this pass narrowly scoped to comments/documentation only
Rejected: Broader Anthropic comment sweep across functional API/protocol references | too ambiguous for a safe prose-only PR
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Leave functional Anthropic references (API behavior, SDKs, URLs, provider labels, protocol docs) for separate reviewed passes
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
* Neutralize remaining internal-only diagnostic labels (#359)
This pass rewrites a small set of ant-only diagnostic and UI labels to
neutral internal wording while leaving command definitions, flags, and
runtime logic untouched. It focuses on internal debug output, dead UI
branches, and noninteractive headings rather than broader product text.
Constraint: Label cleanup only; do not change command semantics or ant-only logic gates
Rejected: Renaming ant-only command descriptions in main.tsx | broader UX surface better handled in a separate reviewed pass
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Remaining ANT-ONLY hits are mostly command descriptions and intentionally deferred user-facing strings
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
* Finish eliminating remaining ANT-ONLY source labels (#360)
This extends the label-only cleanup to the remaining internal-only command,
debug, and heading strings so the source tree no longer contains ANT-ONLY
markers. The pass still avoids logic changes and only renames labels shown
in internal or gated surfaces.
Constraint: Update the existing label-cleanup PR without widening scope into behavior changes
Rejected: Leave the last ANT-ONLY strings for a later pass | low-cost cleanup while the branch is already focused on labels
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: The next phase should move off label cleanup and onto a separately scoped logic or rebrand slice
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
* Stub internal-only recording and model capability helpers (#377)
This follow-up Phase C-lite slice replaces purely internal helper modules
with stable external no-op surfaces and collapses internal elevated error
logging to a no-op. The change removes additional USER_TYPE-gated helper
behavior without touching product-facing runtime flows.
Constraint: Keep this PR limited to isolated helper modules that are already external no-ops in practice
Rejected: Pulling in broader speculation or logging sink changes | less isolated and easier to debate during review
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue Phase C with similarly isolated helpers before moving into mixed behavior files
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
* Remove internal-only bundled skills and mock helpers (#376)
* Remove internal-only bundled skills and mock rate-limit behavior
This takes the next planned Phase C-lite slice by deleting bundled skills
that only ever registered for internal users and replacing the internal
mock rate-limit helper with a stable no-op external stub. The external
build keeps the same behavior while removing a concentrated block of
USER_TYPE-gated dead code.
Constraint: Limit this PR to isolated internal-only helpers and avoid bridge, oauth, or rebrand behavior
Rejected: Broad USER_TYPE cleanup across mixed runtime surfaces | too risky for the next medium-sized PR
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: The next cleanup pass should continue with similarly isolated USER_TYPE helpers before touching main.tsx or protocol-heavy code
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
* Align internal-only helper removal with remaining user guidance
This follow-up fixes the mock billing stub to be a true no-op and removes
stale user-facing references to /verify and /skillify from the same PR.
It also leaves a clearer paper trail for review: the deleted verify skill
was explicitly ant-gated before removal, and the remaining mock helper
callers still resolve to safe no-op returns in the external build.
Constraint: Keep the PR focused on consistency fixes and reviewer-requested evidence, not new cleanup scope
Rejected: Leave stale guidance for a later PR | would make this branch internally inconsistent after skill removal
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When deleting gated features, always sweep user guidance and coordinator prompts in the same pass
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy; changed-file scan still shows only pre-existing tipRegistry errors outside edited lines)
* Clarify generic workflow wording after skill removal
This removes the last generic verification-skill wording that could still
be read as pointing at a deleted bundled command. The guidance now talks
about project workflows rather than a specific bundled verify skill.
Constraint: Keep the follow-up limited to reviewer-facing wording cleanup on the same PR
Rejected: Leave generic wording as-is | still too easy to misread after the explicit /verify references were removed
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When removing bundled commands, scrub both explicit and generic references in the same branch
Tested: bun run build
Tested: bun run smoke
Not-tested: Additional checks unchanged by wording-only follow-up
---------
Co-authored-by: anandh8x <test@example.com>
* test(api): add GEMINI_AUTH_MODE to environment setup in tests
* test: isolate GitHub/Gemini credential tests with fresh module imports and explicit non-bare env setup to prevent cross-test mock/cache leaks
* fix: update GitHub Copilot base URL and model defaults for improved compatibility
* fix: enhance error handling in OpenAI API response processing
* fix: improve error handling for GitHub Copilot API responses and streamline error body consumption
* fix: enhance response handling in OpenAI API shim for better error reporting and support for streaming responses
* feat: enhance GitHub device flow with fresh module import and token validation improvements
* fix: separate Copilot API routing from GitHub Models, clear stale env vars, honor providerOverride.apiKey
* fix: route GitHub GPT-5/Codex to Copilot API, show all Copilot models in picker, clear stale env vars
* fix GitHub Models API regression
* feat: update GitHub authentication to require OAuth tokens, normalize model handling for Copilot and GitHub Models
* fix: update GitHub token validation to support OAuth tokens and improve endpoint type handling
---------
Co-authored-by: Anandan <anandan.8x@gmail.com>
Co-authored-by: anandh8x <test@example.com>
* fix: strip Anthropic-specific params from 3P provider paths
Three silent failure modes affecting all third-party provider users:
1. Thinking blocks serialized as <thinking> text corrupt multi-turn
context — strip them instead of converting to raw text tags.
2. Unknown models fall through to 200k context window default, so
auto-compact never triggers — use conservative 8k for unknown
3P models with a warning log.
3. Session resume with thinking blocks causes 400 or context corruption
on 3P providers — strip thinking/redacted_thinking content blocks
from deserialized messages when resuming against a non-Anthropic
provider.
Addresses findings 2, 3, and 5 from #248.
* test: align resume stripping expectation with orphan-thinking filter
* test: isolate provider env in conversation recovery tests
* test: move provider-sensitive resume coverage behind module mocks
* test: trim extra blank lines in conversation recovery test
Keep the focused provider-resume test diff clean so the regression branch stays easy to review.
Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
---------
Co-authored-by: Claude Opus 4.6 <noreply@openclaude.dev>
* fix: restore Grep and Glob reliability on OpenAI paths
Preserve Grep and Glob pattern fields during OpenAI/Codex schema sanitization, and fall back to system ripgrep when the packaged binary is missing. This keeps search tool schemas intact and improves Linux usability for npm/source installs.
Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
* test: clean up ripgrep fallback test helpers
Remove the unused ripgrepCommand import and normalize mocked builtin ripgrep paths so the test behaves consistently across platforms.
Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
* test: remove duplicate Codex URI schema case
Drop the duplicated WebFetch URI-format test in codexShim.test.ts so test names stay unique and failures remain easier to read.
Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
* test: stabilize ripgrep fallback coverage
Avoid fs/module mocking in ripgrep fallback tests by extracting the config selection logic into a pure helper. This preserves the fallback coverage while removing the test interaction that caused the narrowed Bun hang repro.
Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
* test: tighten ripgrep and schema coverage
Align the ripgrep fallback test with the actual auto-fallback branch, clean up strict typing in schema sanitizer tests, and tighten ripgrep error narrowing for type safety.
Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev>
---------
Co-authored-by: Claude Opus 4.6 <noreply@openclaude.dev>
* fix: address code scanning alerts
Parse Gemini hostnames instead of matching raw URL substrings, redact gRPC error logs, and harden the Finder drag-drop test escape helper so the flagged paths are fixed without regressing working behavior.
* Potential fix for pull request finding 'CodeQL / Clear-text logging of sensitive information'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
* fix: restore safe grpc error summaries
A later autofix commit removed the exported gRPC error summarizer while the new regression test still imported it. Restore the safe name/code-only summary so CI stays green without reintroducing clear-text logging.
* fix: keep grpc logging generic
Remove the stale helper/test pair and keep the gRPC startup and stream logs free of error-derived data so the CodeQL clear-text logging alert stays closed while the rest of the security fixes remain intact.
---------
Co-authored-by: OpenClaude Worker 3 <worker-3@openclaude.local>
Co-authored-by: Vasanth T <148849890+Vasanthdev2004@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
When a proxy is configured, configureGlobalAgents() loads undici to set a
global dispatcher. However, undici v7.24.6 requires Node.js >= 20.18.1 and
references globalThis.File at module evaluation time for webidl type assertions.
Node 18 lacks the File global, causing ReferenceError inside the bundled
__commonJS require chain, which deadlocks due to unresolved circular
dependencies in the module initialization.
Fix by polyfilling globalThis.File early in cli.tsx entrypoint, before any
undici code loads. Try node:buffer.File (available in Node 18.13+), fallback
to minimal Blob-based stub.
Fixes: bun run start hangs indefinitely when HTTP_PROXY/HTTPS_PROXY is set
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code scanning alerts
Parse Gemini hostnames instead of matching raw URL substrings, redact gRPC error logs, and harden the Finder drag-drop test escape helper so the flagged paths are fixed without regressing working behavior.
* Potential fix for pull request finding 'CodeQL / Clear-text logging of sensitive information'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
* fix: restore safe grpc error summaries
A later autofix commit removed the exported gRPC error summarizer while the new regression test still imported it. Restore the safe name/code-only summary so CI stays green without reintroducing clear-text logging.
* fix: keep grpc logging generic
Remove the stale helper/test pair and keep the gRPC startup and stream logs free of error-derived data so the CodeQL clear-text logging alert stays closed while the rest of the security fixes remain intact.
---------
Co-authored-by: OpenClaude Worker 3 <worker-3@openclaude.local>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
## Summary
- Added `isCompactableTool()` helper in `microCompact.ts` that matches
both the existing COMPACTABLE_TOOLS set and any tool prefixed `mcp__`
- MCP tool results were never compacted because the hardcoded allowlist
only contained 9 built-in tools — MCP tools fell through and persisted
in full for the entire session, wasting 10-500K tokens/session
## Impact
- user-facing impact: long sessions using MCP servers (GitHub, Slack,
Playwright, etc.) will compact stale MCP tool results, reducing token
usage and delaying autocompact triggers
- developer/maintainer impact: new MCP servers are automatically covered
via prefix match — no need to update the allowlist per-server
## Testing
- [x] `bun run build`
- [x] `bun run smoke`
- [x] focused tests: `bun test src/services/compact/microCompact.test.ts`
- module exports load correctly
- estimateMessageTokens counts MCP tool_use blocks
- microcompactMessages processes MCP tools without error
- microcompactMessages processes mixed built-in and MCP tools
## Notes
- provider/model path tested: n/a (compaction logic is model-agnostic)
- screenshots attached (if UI changed): n/a
- follow-up work or known limitations: subagent results and thinking
blocks are still not compacted (separate RFCs)
https://claude.ai/code/session_01D7kprMn4c66a5WrZscF7rv
Co-authored-by: Claude <noreply@anthropic.com>
* fix: normalize malformed Bash tool arguments from OpenAI-compatible providers
* fix: keep invalid Bash tool args from becoming commands
* fix: preserve malformed Bash JSON literals
* test: stabilize rebased PR 385 checks
* test: isolate provider profile env assertions
* fix: extend tool argument normalization to all tools and harden edge cases
- Extend STRING_ARGUMENT_TOOL_FIELDS to normalize Read, Write, Edit,
Glob, and Grep plain-string arguments (fixes "Invalid tool parameters"
errors reported by VennDev)
- Normalize streaming Bash args regardless of finish_reason, not only
when finish_reason is 'tool_calls'
- Broaden isLikelyStructuredObjectLiteral to catch malformed object-shaped
strings like {command:"pwd"} and {'command':'pwd'} (fixes CR2 from
Vasanthdev2004)
- Apply blank/object-literal guard to all tools, not just Bash
- Extract duplicated JSON repair suffix combinations into shared constant
- Add 32 isolated unit tests for toolArgumentNormalization
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: skip streaming normalization on finish_reason length
Truncated tool calls (finish_reason: 'length') now preserve the raw
buffer instead of normalizing into executable commands, preventing
incomplete commands from becoming runnable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: comprehensive tool argument normalization hardening
- Remove all { raw: ... } returns that caused InputValidationError with
z.strictObject schemas — return {} instead for clean Zod errors
- Extend normalizeAtStop buffering to all mapped tools (Read, Write,
Edit, Glob, Grep) so streaming paths also get normalized
- Make repairPossiblyTruncatedObjectJson generic — repair any valid
JSON object, not just ones with a command field
- Export hasToolFieldMapping for streaming normalizeAtStop decision
- Skip normalization on finish_reason: length to preserve raw truncated
buffer
- Update all test expectations to match new behavior
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix GLM-5 and other reasoning models appearing to hang via OpenAI shim
Reasoning models like GLM-5 and DeepSeek stream chain-of-thought in
`reasoning_content` while `content` stays empty (""). The OpenAI shim
only read `delta.content`, so it saw empty strings and never emitted
any Anthropic stream events — causing the UI to appear frozen.
- Add `reasoning_content` to streaming chunk and non-streaming response types
- Emit `reasoning_content` as thinking blocks (thinking_delta) in streaming mode
- Properly transition from thinking to text blocks when content phase begins
- Fall back to `reasoning_content` in non-streaming mode when content is null
Fixes#214
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix non-streaming reasoning_content fallback and add tests
- Use explicit empty-string check instead of || for content fallback
so content: "" doesn't leak reasoning_content as visible text
- Close thinking block before tool call blocks in streaming path
- Add non-streaming and streaming reasoning_content tests
Co-Authored-By: GLM-5.1 <noreply@openclaude.dev>
* Fix flaky Ink reconciler tests caused by react-compiler memoization
Remove hard throw in createTextInstance that crashed when hostContext.isInsideText
was stale due to react-compiler element caching. Add timeout guards to prevent
test hangs when render errors prevent exit() from firing.
Co-Authored-By: Claude GLM-5.1 <noreply@openclaude.dev>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: GLM-5.1 <noreply@openclaude.dev>
* docs(docs): add agent guidance and repository instructions
- Created `AGENTS.md` and `CLAUDE.md` to provide high-signal guidance for AI agents and developers working in the repository.
- Outlined critical developer commands for building, testing, and running diagnostics using `bun`.
- Documented the repository architecture, source entrypoints, and core service logic.
- Defined framework-specific quirks, including module stubbing for internal modules and macro versioning.
- Established style and workflow guidelines regarding telemetry, environment variables, and security scan requirements.
* feat(api): support gemini thought signatures in openai shim
- Added `isGeminiMode` utility to detect Gemini backends via `CLAUDE_CODE_USE_GEMINI` or `OPENAI_BASE_URL`.
- Updated `convertMessages` to extract `thought_signature` from thinking blocks and inject them into tool calls.
- Implemented a fallback mechanism that provides a `skip_thought_signature_validator` string to avoid 400 validation errors when a signature is missing.
- Enhanced `openaiStreamToAnthropic` and `OpenAIShimMessages` to correctly preserve and pass through Gemini-specific metadata in `extra_content`.
* refactor(api): improve gemini metadata handling and remove redundant docs
- Updated `src/services/api/openaiShim.ts` to merge existing `google`-specific metadata within `extra_content` instead of overwriting it.
- Simplified the `thought_signature` assignment logic to use a fallback value of `skip_thought_signature_validator` when no signature is provided.
- Deleted `AGENTS.md` and `CLAUDE.md` files to eliminate redundant agent guidance documentation.
* fix(api): propagate gemini thought signatures to all parallel tool calls
- Removed the index constraint when assigning the `signature` from a `thinkingBlock` to tool calls in `openaiShim.ts`.
- Ensured that the `thought_signature` is applied to every tool call in a parallel set, rather than just the first one.
- Aligned the shim with Gemini API requirements, which mandate that the same signature must be present on every replayed function call part within an assistant turn.
* gRPC Server
* gRPC fix
* UpdProto
* fix: address PR review feedback for gRPC server
- Update bun.lock for new dependencies (frozen-lockfile CI fix)
- Add multi-turn session persistence via initialMessages
- Replace hardcoded done payload with real token counts
- Default bind to localhost instead of 0.0.0.0
* fix(grpc): startup parity, cancel interrupt, and cli text fallback
- Replace enableConfigs() with await init() in start-grpc.ts for full
bootstrap parity with the main CLI (env vars, CA certs, mTLS, proxy,
OAuth, Windows shell)
- Call engine.interrupt() before call.end() in the cancel handler so
in-flight model/tool execution is actually stopped
- Show done.full_text in the CLI client when no text_chunk was received,
preventing silent drops when streaming is unavailable
* fix(grpc): wire session_id end-to-end and remove dead provider field
- Move session_id from ClientMessage into ChatRequest to fix proto-loader
oneofs encoding bug and make the field functional
- Implement in-memory session store so reconnecting with the same
session_id resumes conversation context across streams
- Remove ChatRequest.provider — per-request provider routing requires
global process.env mutation, unsafe for concurrent clients; provider
is configured via env vars at server startup
* fix(grpc): mirror CLI auth bootstrap in start-grpc and fix tool_name field
scripts/start-grpc.ts now runs the same provider/auth bootstrap as the
normal CLI entrypoint: enableConfigs, safe env vars, Gemini/GitHub token
hydration, saved-profile resolution with warn-and-fallback, and provider
validation before the server binds.
ToolCallResult.tool_name was being populated with the tool_use_id UUID.
Added a toolNameById map (filled in canUseTool) so tool_name now carries
the actual tool name (e.g. "Bash"). The UUID moves to a new tool_use_id
field (proto field 4) for client-side correlation.
* fix(grpc): add tool_use_id to ToolCallStart and interrupt engine on stream close
Two blocker-level issues flagged in code review:
- ToolCallStart was missing tool_use_id, making it impossible for clients
to correlate tool_start events with tool_result when the same tool runs
multiple times. Added tool_use_id = 3 to the proto message and populated
it from the toolUseID parameter in canUseTool.
- On stream close without an explicit CancelSignal the server only nulled
the engine reference, leaving the underlying model/tool work running
as an orphan. Added engine.interrupt() in the call.on('end') handler
to stop work immediately when the client disconnects.
* fix(grpc): resolve pending promises on disconnect and guard post-cancel writes
Four lifecycle and contract issues identified during proactive review:
- Pending permission Promises in canUseTool would hang forever if the
client disconnected mid-stream. On call 'end', all pending resolvers
are now called with 'no' so the engine can unblock and terminate.
- The done message and session save could fire after call.end() when
a CancelSignal arrived mid-generation. Added an `interrupted` flag
set on both cancel and stream close to gate all post-loop writes.
- The session map had no eviction policy, allowing unbounded memory
growth. Capped at MAX_SESSIONS=1000 with FIFO eviction of the
oldest entry.
- Field 3 was silently absent from ChatRequest. Added `reserved 3`
to document the gap and prevent accidental reuse in future.
* fix(grpc): reset previousMessages on each new request to prevent session history leak
previousMessages was declared at stream scope and only overwritten when
the incoming session_id already existed in the session store. A second
request on the same stream with a new session_id would silently inherit
the first request's conversation history in initialMessages instead of
starting fresh, violating the session contract.
Fix: reset previousMessages to [] at the start of each ChatRequest
before the session-store lookup.
* fix(grpc): reset interrupted flag between requests and guard against concurrent ChatRequest
Two stream-scoped state bugs found during proactive audit:
- The `interrupted` flag was never reset between requests on the same
stream. If the first request was cancelled, all subsequent requests
would silently skip the done message, causing the client to hang.
- A second ChatRequest arriving while the first was still processing
would overwrite the engine reference, corrupting the lifecycle of
both requests. Now returns ALREADY_EXISTS error instead. Engine is
nulled after the for-await loop completes so subsequent requests
can proceed normally.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: convert dragged file paths to @mentions for attachment
When non-image files are dragged into the terminal, the file path was
inserted as plain text and never attached. Now detected absolute paths
are converted to @mentions so they get picked up by the attachment system.
* test: add tests for drag-and-drop file path detection
* fix: multi-image drag-and-drop only showing last image
insertTextAtCursor read input and cursorOffset from the React closure,
which is stale when called in a synchronous loop (e.g. onImagePaste for
multiple dragged images). Now uses refs so each insertion chains on the
previous one.
* fix: quote Windows absolute paths to avoid MCP mention collision
Paths containing ':' (e.g. Windows drive letters) are now emitted in
quoted @"..." form so they don't match the MCP resource mention regex.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: decouple dragDropPaths from imagePaste and harden image checks
- Check image extension against the cleaned path (post quote/escape
stripping) so quoted or backslash-escaped image drops are reliably
routed to the image paste handler.
- Inline the image extension regex and drop the imagePaste/fsOperations
imports so the module (and its tests) no longer pull in `bun:bundle`
and the heavier fs wrapper chain. Use plain `fs.existsSync` for the
on-disk check.
- Add tests covering quoted image paths, uppercase extensions,
backslash-escaped image paths, escaped real files with spaces, mixed
segments containing an image, quoted-nonexistent paths, and leading
or trailing whitespace.
* test: verify dragged paths with an `@` segment are preserved
Adds a fixture under a scoped-package-style subdir (`@types/index.d.ts`)
so we exercise the realistic `node_modules/@types/...` drag case and
lock in that `extractDraggedFilePaths` returns the raw path unchanged —
the `@` inside the path must not collide with the mention prefix the
caller prepends downstream.
* test: parametrize dragDropPaths cases with test.each
Groups the 21 scenarios into four table-driven describes
(empty-result, single-path, multi-path, backslash-escaped) so that
adding a new case is a one-line row instead of a new `test()` block.
Fixture directories are now created synchronously at describe-load
time so their paths are available to the test.each tables, which are
built before any hook runs.
* test: add contract tests for @-mention extractor boundary
Pins the contract between `extractAtMentionedFiles` and
`extractMcpResourceMentions` so the MCP regex can't silently swallow
quoted file-path mentions.
These tests fail on current HEAD — 3 of 11 cases expose the regression
pointed out in the review on #382: `extractMcpResourceMentions`'s
trailing `\b` backtracks past the closing `"` of a quoted mention and
produces a ghost match for `@"C:\Users\..."`, `@C:\Users\...`, and
`@"/tmp/weird:name.txt"`. The remaining 8 cases lock in the behaviour
that must not change (legitimate `server:resource` mentions and plain
file-path mentions).
Committed failing on purpose as the first half of a test-then-fix
pair; the regex fix follows in a subsequent commit.
* fix: prevent MCP extractor from ghost-matching quoted/Windows paths
The MCP resource regex used `\b` as a trailing anchor with `[^\s]+`
character classes. On any quoted file mention containing a colon
(`@"C:\Users\me\file.txt"`, `@"/tmp/weird:name.txt"`), the engine
backtracked past the closing `"` to satisfy `\b`, producing a ghost
match that collided with `extractAtMentionedFiles`. Unquoted Windows
drive-letter paths (`@C:\Users\me\file.txt`) also matched because a
drive letter is structurally identical to an MCP `server:resource`
token.
Two guards:
1. `(?!")` right after `@` drops quoted tokens entirely, and adding
`"` to the character classes blocks any mid-match backtracking.
2. A post-match filter discards `^[A-Za-z]:[\\/]` — a single-letter
server followed by a path separator is always a Windows drive
prefix, never a real MCP resource.
Legitimate MCP forms (`@server:resource/path`, plugin-scoped like
`@asana-plugin:project-status/123`, inline prose mentions) remain
matched and are pinned by the contract tests added in 04998d5.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add LiteLLM proxy setup guide
Document the setup process for LiteLLM and its integration with OpenClaude, including prerequisites, configuration, and troubleshooting steps.
* Revise LiteLLM setup steps for Adocs: fix /provider walkthrough to match actual OpenAI-compatible flowPI key and model
Updated setup instructions for LiteLLM provider configuration.
* docs: fix sub-bullet formatting in /provider steps
* docs: clarify key scope in troubleshooting (LiteLLM proxy process env)
Clarified instruction for upstream provider error regarding API key.
Models served through Ollama/vLLM with strict Jinja templates (Devstral,
Mistral, etc.) require strict user↔assistant role alternation and reject
requests with consecutive messages of the same role.
convertMessages() could produce consecutive user or assistant messages in
three scenarios: batched user input, text-only + tool_use assistant turns,
and tool result remainders followed by another user message.
Added a coalescing pass at the end of convertMessages() that merges
consecutive same-role messages (string concat or array concat), preserving
tool_calls on assistant messages. Tool and system messages are excluded
from coalescing as they have their own alternation rules.
Includes regression tests for both user and assistant coalescing.
Fixes#202
* Add local OpenAI-compatible model discovery to /model
* Guard local OpenAI model discovery from Codex routing
* Preserve remote OpenAI Codex alias behavior
Models not in the lookup table fall through to a 200k default, causing
auto-compact to never trigger for models with smaller actual context
windows. Users hit hard context_window_exceeded errors instead.
Added to both context window and max output token tables:
- o1, o1-mini, o1-preview, o1-pro (OpenAI reasoning models)
- llama3.2:1b, qwen3:8b, codestral (common Ollama models)
Relates to #248
* security: force lodash-es 4.18.0 for transitive dependencies
PR #225 bumped the direct lodash-es dependency to 4.18.0, but
@anthropic-ai/sandbox-runtime still pulled lodash-es@4.17.23 via its
own ^4.17.23 range. The transitive copy was vulnerable to:
- HIGH: Code Injection via _.template (GHSA-r5fr-rjxr-66jc)
- MODERATE: Prototype Pollution via _.unset/_.omit (GHSA-f23m-r3pf-42rh)
Added overrides field in package.json to force all copies to 4.18.0.
bun audit now reports zero vulnerabilities.
* fix: use lodash-es 4.18.1 instead of deprecated 4.18.0
lodash-es 4.18.0 is explicitly deprecated by the maintainer with
the message "Bad release. Please use lodash-es@4.17.23 instead."
Updated both the direct dependency and the override to 4.18.1, which
is the latest non-deprecated release that patches the CVEs.
Reproduction:
- Enable `frontend-design@claude-code-plugins`
- Enable `frontend-design@claude-plugins-official`
- Start OpenClaude with both marketplace plugins active
- Both plugins load, but downstream command and skill scopes key off the short plugin name, so both collapse to `frontend-design` and can interfere with interactive startup
Fix:
- Collapse duplicate marketplace plugins by short name during merge
- Keep the enabled copy when enabled state differs; otherwise keep the later config entry
- Add regression coverage for both cases
## Summary
- Extract retry-after header from 429 API errors and include timing
guidance in the user-facing error message
- Previously, non-quota 429 errors showed a generic message with no
guidance on when to retry, only a link to status.anthropic.com
## Impact
- user-facing impact: 429 error messages now tell users when to retry
instead of just linking to a status page
- developer/maintainer impact: none
## Testing
- [x] `bun run build`
- [ ] `bun run smoke`
- [ ] focused tests: error formatting is pure string construction,
verified via build + manual inspection
## Notes
- provider/model path tested: applies to all providers returning 429
- screenshots attached (if UI changed): n/a
- follow-up work or known limitations: 529 errors could get similar
treatment in a follow-up
https://claude.ai/code/session_01D7kprMn4c66a5WrZscF7rv
Co-authored-by: Claude <noreply@anthropic.com>
* Remove internal-only bundled skills and mock rate-limit behavior
This takes the next planned Phase C-lite slice by deleting bundled skills
that only ever registered for internal users and replacing the internal
mock rate-limit helper with a stable no-op external stub. The external
build keeps the same behavior while removing a concentrated block of
USER_TYPE-gated dead code.
Constraint: Limit this PR to isolated internal-only helpers and avoid bridge, oauth, or rebrand behavior
Rejected: Broad USER_TYPE cleanup across mixed runtime surfaces | too risky for the next medium-sized PR
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: The next cleanup pass should continue with similarly isolated USER_TYPE helpers before touching main.tsx or protocol-heavy code
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
* Align internal-only helper removal with remaining user guidance
This follow-up fixes the mock billing stub to be a true no-op and removes
stale user-facing references to /verify and /skillify from the same PR.
It also leaves a clearer paper trail for review: the deleted verify skill
was explicitly ant-gated before removal, and the remaining mock helper
callers still resolve to safe no-op returns in the external build.
Constraint: Keep the PR focused on consistency fixes and reviewer-requested evidence, not new cleanup scope
Rejected: Leave stale guidance for a later PR | would make this branch internally inconsistent after skill removal
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When deleting gated features, always sweep user guidance and coordinator prompts in the same pass
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy; changed-file scan still shows only pre-existing tipRegistry errors outside edited lines)
* Clarify generic workflow wording after skill removal
This removes the last generic verification-skill wording that could still
be read as pointing at a deleted bundled command. The guidance now talks
about project workflows rather than a specific bundled verify skill.
Constraint: Keep the follow-up limited to reviewer-facing wording cleanup on the same PR
Rejected: Leave generic wording as-is | still too easy to misread after the explicit /verify references were removed
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When removing bundled commands, scrub both explicit and generic references in the same branch
Tested: bun run build
Tested: bun run smoke
Not-tested: Additional checks unchanged by wording-only follow-up
---------
Co-authored-by: anandh8x <test@example.com>
This follow-up Phase C-lite slice replaces purely internal helper modules
with stable external no-op surfaces and collapses internal elevated error
logging to a no-op. The change removes additional USER_TYPE-gated helper
behavior without touching product-facing runtime flows.
Constraint: Keep this PR limited to isolated helper modules that are already external no-ops in practice
Rejected: Pulling in broader speculation or logging sink changes | less isolated and easier to debate during review
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue Phase C with similarly isolated helpers before moving into mixed behavior files
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
This extends the label-only cleanup to the remaining internal-only command,
debug, and heading strings so the source tree no longer contains ANT-ONLY
markers. The pass still avoids logic changes and only renames labels shown
in internal or gated surfaces.
Constraint: Update the existing label-cleanup PR without widening scope into behavior changes
Rejected: Leave the last ANT-ONLY strings for a later pass | low-cost cleanup while the branch is already focused on labels
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: The next phase should move off label cleanup and onto a separately scoped logic or rebrand slice
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
This pass rewrites a small set of ant-only diagnostic and UI labels to
neutral internal wording while leaving command definitions, flags, and
runtime logic untouched. It focuses on internal debug output, dead UI
branches, and noninteractive headings rather than broader product text.
Constraint: Label cleanup only; do not change command semantics or ant-only logic gates
Rejected: Renaming ant-only command descriptions in main.tsx | broader UX surface better handled in a separate reviewed pass
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Remaining ANT-ONLY hits are mostly command descriptions and intentionally deferred user-facing strings
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
This is a small prose-only follow-up that rewrites clearly internal or
explanatory Anthropic comment language to neutral wording in a handful of
high-confidence files. It avoids runtime strings, flags, command labels,
protocol identifiers, and provider-facing references.
Constraint: Keep this pass narrowly scoped to comments/documentation only
Rejected: Broader Anthropic comment sweep across functional API/protocol references | too ambiguous for a safe prose-only PR
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Leave functional Anthropic references (API behavior, SDKs, URLs, provider labels, protocol docs) for separate reviewed passes
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
This pass rewrites comment-only ANT-ONLY markers to neutral internal-only
language across the source tree without changing runtime strings, flags,
commands, or protocol identifiers. The goal is to lower obvious internal
prose leakage while keeping the diff mechanically safe and easy to review.
Constraint: Phase B is limited to comments/prose only; runtime strings and user-facing labels remain deferred
Rejected: Broad search-and-replace across strings and command descriptions | too risky for a prose-only pass
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Remaining ANT-ONLY hits are mostly runtime/user-facing strings and should be handled separately from comment cleanup
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (upstream baseline remains noisy)
Co-authored-by: anandh8x <test@example.com>
* Remove internal-only tooling without changing external runtime contracts
This trims the lowest-risk internal-only surfaces first: deleted internal
modules are replaced by build-time no-op stubs, the bundled stuck skill is
removed, and the insights S3 upload path now stays local-only. The privacy
verifier is expanded and the remaining bundled internal Slack/Artifactory
strings are neutralized without broad repo-wide renames.
Constraint: Keep the first PR deletion-heavy and avoid mass rewrites of USER_TYPE, tengu, or claude_code identifiers
Rejected: One-shot DMCA cleanup branch | too much semantic risk for a first PR
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Treat full-repo typecheck as a baseline issue on this upstream snapshot; do not claim this commit introduced the existing non-Phase-A errors without isolating them first
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Not-tested: Full repo typecheck (currently fails on widespread pre-existing upstream errors outside this change set)
* Keep minimal source shims so CI can import Phase A cleanup paths
The first PR removed internal-only source files entirely, but CI provider
and context tests import those modules directly from source rather than
through the build-time no-telemetry stubs. This restores tiny no-op source
shims so tests and local source imports resolve while preserving the same
external runtime behavior.
Constraint: GitHub Actions runs source-level tests in addition to bundled build/privacy checks
Rejected: Revert the entire deletion pass | unnecessary once the import contract is satisfied by small shims
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: For later cleanup phases, treat build-time stubs and source-test imports as separate compatibility surfaces
Tested: bun run build
Tested: bun run smoke
Tested: bun run verify:privacy
Tested: bun run test:provider
Tested: bun run test:provider-recommendation
Not-tested: Full repo typecheck (still noisy on this upstream snapshot)
---------
Co-authored-by: anandh8x <test@example.com>
Remove debug systems, employee detection, and internal logging
that have no function in a community fork.
Changes:
- Remove logPermissionContextForAnts import and calls (main.tsx, compact.ts)
Reads Kubernetes namespace and container IDs from internal infra paths.
Dead code for all external users.
- Remove createDumpPromptsFetch import and gate (query.ts)
Internal prompt dump system for employee debugging.
Replace gate with unconditional undefined — normal fetch always used.
- Remove stripSignatureBlocks ant-only block (query.ts)
Was behind USER_TYPE === 'ant' guard, never ran for external users.
- Hardcode isAnt: false (query/config.ts)
Employee detection flag has no place in a community fork.
config.gates.isAnt had exactly one consumer (dumpPromptsFetch, now removed).
- Gut logClassifierResultForAnts body (bashPermissions.ts)
Replace with empty no-op. Still called from 4 sites, zero execution.
Remove ANT-ONLY comments describing internal security model.
- Gate status.anthropic.com behind firstParty check (errors.ts)
429 error hint now only shown when using Anthropic directly.
Third-party provider users see a generic capacity message.
Build: passes
Typecheck: clean (no new errors)
Tests: 196 pass, same 6 pre-existing failures unrelated to these changes
* fix: auto-allow safe read-only commands in acceptEdits mode
In acceptEdits mode, read-only commands like grep, cat, ls, find, head,
tail were still prompting for approval. This created unnecessary friction
since these commands cannot modify or delete files.
Add safe read-only commands to ACCEPT_EDITS_ALLOWED_COMMANDS:
grep, cat, ls, find, head, tail, echo, pwd, wc, sort, uniq, diff
These are all read-only — they cannot cause data loss or modify the
filesystem. Auto-allowing them reduces approval fatigue in acceptEdits
mode without introducing any safety risk.
Write commands (rm, rmdir, mv, cp, sed, mkdir, touch) are unchanged.
The dangerous path guard for rm/rmdir remains in place.
Fixes#251.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(bash): block unsafe acceptEdits auto-allow
Keep the new read-only acceptEdits commands behind the existing read-only validator and block shell redirection based on the original command text. This prevents commands like echo > file and find -delete from being silently auto-approved while preserving safe read-only commands.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
On Windows, shells can set OPENAI_BASE_URL to the literal string
"undefined" when the variable is referenced without quotes while unset.
The nullish-coalescing operator (??) does not catch this because
"undefined" is a truthy string, causing resolveProviderRequest() to
treat it as a real base URL. This broke the Codex transport check:
(!rawBaseUrl && isCodexAlias(model)) evaluated as (false || true) = false
so the transport was incorrectly set to chat_completions (issue #336).
Fix: introduce asEnvUrl() which trims the value and rejects both empty
strings and the sentinel string "undefined". Use it for all three
rawBaseUrl sources (options.baseUrl, OPENAI_BASE_URL, OPENAI_API_BASE).
Tests: add three new cases to the 'Codex provider config' describe block
covering the empty-string, "undefined"-string, and options-override
scenarios. Also add beforeEach/afterEach guards so individual tests
cannot contaminate each other via env var state.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
In acceptEdits mode, filesystem commands (rm, rmdir, mv, cp, sed, mkdir,
touch) were returned as 'allow' before checkDangerousRemovalPaths ran.
This meant rm -rf ~ and rm -rf / bypassed the dangerous path guard entirely.
Fix:
- Export checkDangerousRemovalPaths from pathValidation.ts
- In modeValidation.ts, call it for rm/rmdir before returning allow
- Safe paths (rm file.txt) continue to auto-allow unchanged
- Dangerous paths (rm -rf ~) now return 'ask' requiring user approval
This is a defense-in-depth guard that matters most for 3P models (local
Ollama, DeepSeek etc.) that lack built-in refusal training and would
blindly execute destructive commands in acceptEdits mode.
Fixes finding 3 from issue #244.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Inline base64 source maps had been checked into tracked src files. This strips those comments from the repository without changing runtime behavior or adding ongoing guardrails, per the requested one-time cleanup scope.
Constraint: Keep this change limited to tracked source cleanup only
Rejected: Add CI/source verification guard | user requested one-time cleanup only
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If these directives reappear, fix the producing transform instead of reintroducing repo-side cleanup code
Tested: rg -n "sourceMappingURL" ., bun run smoke, bun run verify:privacy, bun run test:provider, npm run test:provider-recommendation
Not-tested: bun run typecheck (repository has many pre-existing unrelated failures)
Co-authored-by: anandh8x <test@example.com>
Keep normal prompt submissions during generation queued instead of interrupting the current turn. Add a visible next-turn banner in the prompt area so users can tell their follow-up guidance was accepted, and cover the new behavior with focused tests.
Fixes#328
Co-authored-by: Claude <noreply@anthropic.com>
Two provider routing bugs that cause silent wrong-model failures:
1. model.ts: getUserSpecifiedModelSetting() read ANTHROPIC_MODEL ||
GEMINI_MODEL || OPENAI_MODEL with no provider check. A user
switching from Anthropic to OpenAI with ANTHROPIC_MODEL still set
would silently send the Anthropic model name to the OpenAI API.
Now gates each env var behind the active provider from
getAPIProvider().
2. providers.ts: isCodexModel() maintained a hardcoded list of 8 model
names that was missing gpt-5.4-mini and gpt-5.2 from the canonical
CODEX_ALIAS_MODELS table in providerConfig.ts. This caused a
split-brain: getAPIProvider() returned 'openai' while
resolveProviderRequest() selected 'codex_responses' transport.
Now delegates to the exported isCodexAlias() to keep both detection
systems in sync.
* feat: fix open-source build and add Ollama model picker
- Fix build failures by stubbing 62+ missing Anthropic-internal modules
with a catch-all plugin in scripts/build.ts
- Add runtime shim exports (isReplBridgeActive, getReplBridgeHandle) in
bootstrap/state.ts for feature-gated code references
- Add /model picker support for Ollama: fetches available models from
Ollama server at startup and displays them in the model selection menu
- Add Ollama model validation against cached server model list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review feedback for Ollama integration
- Move Ollama validation before enterprise allowlist check in validateModel
- Truncate model list in error messages to first 5 entries
- Fix isOllamaProvider() to detect OLLAMA_BASE_URL-only configurations
- Reuse getOllamaApiBaseUrl() from providerDiscovery instead of duplicating
- Reset fetchPromise on failure to allow retry in prefetchOllamaModels
- Include Default option in Ollama model picker, prevent Claude model fallthrough
- Add file existence check for src/tasks/ stubs in build script
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use pre-scanned exact-match resolvers to avoid Bun bundler corruption
Bun's onResolve plugin corrupts the module graph even when returning null
for non-matching imports. This caused lodash-es memoize and zod's util
namespace to be incorrectly tree-shaken, producing runtime ReferenceErrors.
Replace all pattern-based onResolve hooks with a pre-build scan that
identifies missing modules upfront, then registers exact-match resolvers
only for confirmed missing imports. This avoids touching any valid module
resolution paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: move Ollama model prefetch outside startup throttle gate
prefetchOllamaModels() was inside the skipStartupPrefetches condition,
so it would be skipped on subsequent launches due to the bgRefresh
throttle timestamp. Ollama model fetch targets a local/remote server
and is fast & cheap, so it should always run at startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: OAuth tokens secure storage for Windows & Linux
* fix: OAuth tokens secure storage for Windows & Linux #215
* fix: OAuth tokens secure storage for Windows & Linux #215
* fix: OAuth tokens secure storage for Windows & Linux #215
This sixth pass continues issue #314 with a message-focused micro-batch: three tiny message components that each only surfaced a single unused React import warning. The batch stays intentionally minimal and behavior-neutral.
Constraint: Keep pass 6 limited to one-line message-component cleanup with the same warning shape
Rejected: Mix in broader message component cleanup or unrelated typing fixes | would dilute the micro-pass and expand review scope unnecessarily
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue splitting by path + warning shape when easy one-line cleanups are available
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)
Co-authored-by: anandh8x <test@example.com>
This fifth cleanup pass follows issue #314 with another homogeneous, low-risk slice: three LogoV2 notice/upsell components that each only surfaced a single unused React import warning. Removing just those imports keeps the series moving without mixing in broader cleanup categories.
Constraint: Keep pass 5 to files with the same single-warning pattern
Rejected: Fold in unrelated LogoV2 files with wider warnings or other cleanup shapes | would make the pass less uniform and less reviewable
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue preferring single-pattern micro-passes when the compiler output presents them clearly
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)
Co-authored-by: anandh8x <test@example.com>
This fourth pass continues issue #314 with the smallest cleanup slice so far: six files that each surfaced the same single warning for an unused React import. Keeping the batch this focused improves review speed and removes noise without changing behavior or widening the cleanup scope.
Constraint: Limit this pass to single-warning files only so the intent stays obvious
Rejected: Combine this with broader typing or props-alias cleanup | unnecessary scope expansion for an already reviewable one-line pattern
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep using ultra-small homogeneous passes whenever the compiler output exposes them; they are cheap to review and easy to revert
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)
Co-authored-by: anandh8x <test@example.com>
This third pass continues the issue #314 cleanup series with four very small display-oriented components. The changes only remove unused React imports and reconnect existing Props aliases or parameter types where the files were otherwise surfacing straightforward compiler noise.
Constraint: Keep the pass limited to trivial display components with uniform low-risk cleanup shape
Rejected: Mix in files with unrelated missing-module or broader logic noise | weakens review focus and muddies verification
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Prefer these tiny homogeneous slices while the repo still has wider baseline no-unused noise
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)
Co-authored-by: anandh8x <test@example.com>
This follow-up pass continues the phased unused-code cleanup from issue #314 with four dialog components that shared the same low-risk pattern: avoidable unused imports, dormant Props aliases, and untyped callback plumbing that only existed as compiler noise. The changes keep behavior intact while reducing the next layer of cleanup friction.
Constraint: Keep the second pass narrowly scoped and homogeneous so it stays easy to review beside PR #316
Rejected: Fold in EffortCallout and wider component cleanup at the same time | larger surface area and less uniform risk profile
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue batching future passes by shared cleanup pattern; leave broader refactors and compatibility placeholders for separate PRs
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)
Co-authored-by: anandh8x <test@example.com>
This first cleanup pass removes clearly unused imports and dead locals from a small set of components, and reconnects a few existing Props aliases to their component signatures so they stop surfacing as avoidable noise. The scope stays intentionally narrow to make the follow-up cleanup series easier to review and lower risk.
Constraint: Follow issue #314 with a components-only, low-risk first pass
Rejected: Broader sweep across commands/hooks/utils in the same PR | too much review surface for an initial cleanup pass
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep subsequent unused-code cleanups narrowly batched; treat signature/compatibility placeholders separately from straightforward import/alias cleanup
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (baseline repo noise remains outside this narrow pass)
Co-authored-by: anandh8x <test@example.com>
This seventh pass continues issue #314 with a two-file PromptInput micro-batch. Both files shared the same single-warning pattern for an unused React import, so the cleanup stays extremely small, behavior-neutral, and easy to review.
Constraint: Keep pass 7 to the exact two-file PromptInput pair with the same warning shape
Rejected: Fold in broader PromptInput cleanup or unrelated warnings | would expand scope beyond a simple micro-pass
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Continue harvesting paired file/test micro-batches when the compiler output exposes them cleanly
Tested: bun run build; bun run smoke; targeted noUnused grep for touched files via bun x tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false
Not-tested: full repo typecheck (broader baseline noise remains outside this pass)
Co-authored-by: anandh8x <test@example.com>
* added duck duck go for websearch tools that allowed free searching
* update readme
* Replace @phukon/duckduckgo-search with duck-duck-scrape and fix Firecrawl routing priority, and add DDG error handling
* refactor: streamline DuckDuckGo search fallback to use Firecrawl directly on rate limit
* docs: update README to clarify DuckDuckGo web search fallback and its limitations with TOS
* fix: disable experimental API betas by default to prevent 500 errors
Tool search (defer_loading), global cache scope, and context management
betas require internal Anthropic server-side support. External accounts
receive 500 Internal Server Error when these are sent.
Set CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=true by default in the CLI
entrypoint. Users with internal access can opt back in with =false.
Also includes: cache key stability fixes (Sonnet 1M latch, system-before-
messages key ordering, resume fingerprint isMeta skip), sideQuery default
cleanup, and /dream command.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: standardize API headers to Headers type and enable tengu feature flags by default
* fix: address PR review — dream lock, MCP betas guard, redundant Partial
- Call recordConsolidation() programmatically in /dream instead of
delegating to model prompt (unreliable)
- Add CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS guard to MCP entrypoint
(was only in CLI entrypoint, causing 500s in MCP server mode)
- Remove redundant ? markers from SecretValueSource Partial<{}> type
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three compounding issues caused keyboard input to appear frozen or drop
characters on startup, particularly on Windows CMD/PowerShell and Mac
terminal environments.
Issue 1 — earlyInput disabled (cli.tsx):
The rebrand commit gated startCapturingEarlyInput() behind an opt-in
env flag (OPENCLAUDE_ENABLE_EARLY_INPUT=1), which meant any characters
typed before React finished mounting were silently dropped. Users who
type immediately after launch saw an empty input box with no indication
their keystrokes were lost. Flipped to an opt-out flag
(OPENCLAUDE_DISABLE_EARLY_INPUT=1) so early input capture is on by
default, matching the original upstream behaviour.
Issue 2 — stdin.resume() called before listener attached (App.tsx):
stdin.resume() put the stream into flowing mode before the data/readable
listener was registered. Any input arriving in that gap was queued and
delivered in a burst when the listener connected, which could flood
React's scheduler and stall input processing. Moved resume() to after
the listener is attached so the stream only flows once the handler is
ready.
Issue 3 — AnimatedAsterisk fires ~60 React re-renders in 3s (AnimatedAsterisk.tsx):
The startup screen colour sweep animation runs at 50ms intervals for
3000ms total. Each tick triggers a full re-render of the startup screen
subtree, which competes with stdin event processing in React's microtask
queue. On Windows, where the event loop scheduler is slower, this
reliably caused typing to lag or freeze for the first few seconds after
launch. The animation is now skipped on Windows (process.platform ===
'win32'), showing the icon in its settled state immediately. Mac and
Linux are unaffected.
Closes#228, #220, #205
Prevents collision with existing Claude Code installations that already
use ~/.claude for their own config, settings, and project data.
Migration compatibility: if ~/.openclaude does not yet exist but ~/.claude
does, the legacy path is kept automatically so existing openclaude users
don't lose their data on upgrade. New installs go straight to ~/.openclaude.
Users who need an explicit path can set CLAUDE_CONFIG_DIR.
Fixes#184
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
getPromptCachingEnabled() returned true for all providers including
Azure Foundry, OpenAI, Gemini, and GitHub. This caused cache_control
blocks to be injected into every request sent to 3P endpoints.
Azure Foundry's strict Responsible AI content filter treats unexpected
Anthropic-specific fields (cache_control: { type: "ephemeral" }) as
a jailbreak signal and rejects the request with a 400 error — even
for a simple prompt like "hi".
Fix: return false early when provider is not firstParty, bedrock, or
vertex — the only providers that understand and support prompt caching.
Fixes#273
Related: #267 (Finding 1)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: resolve keyboard freeze via sync render path and stable useAppState selectors
Two compounding React 19 defects caused keyboard input to freeze after
MCP notifications or rapid state updates:
Defect 2 (ink.tsx): The render() path used async updateContainer, which
batches updates across scheduler ticks. Keyboard events dispatched mid-render
drained faster than React processed them, causing input to appear frozen.
Fixed by switching to updateContainerSync + flushSyncWork (same pattern
already used in the unmount path).
Defect 4 (AppState.tsx): useAppState and useAppStateMaybeOutsideOfProvider
used React Compiler _c cache invalidation tied to selector identity. Inline
arrow selectors (new reference each render) invalidated the cache every cycle,
producing a new `get` function. useSyncExternalStore treats a new `get` as a
tearing signal, re-syncing state and re-rendering — causing a loop that
starved the input handler. Fixed with useRef + useCallback(fn, []) to give
useSyncExternalStore a permanently stable snapshot reference.
Note: AppState.tsx is React Compiler output. The _c bypass for these two
hooks is intentional — compiler cache invalidation on inline selectors is
the root cause of the tearing loop.
All 200 tests pass. Build and smoke test verified.
Closes#77, #220, #228
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update selector refs during render instead of useLayoutEffect
Addresses review feedback on PR #266. The previous useLayoutEffect approach
updated selectorRef.current after the render phase, meaning a changed selector
(e.g. s => s.tasks[attachment.taskId] when taskId changes) would still read
stale data during the render it changed in.
Fix: assign selectorRef.current and storeRef.current directly during render
before useSyncExternalStore calls get(). Ref mutation during render is safe
here — it's synchronous and happens before the snapshot is read. get()
identity stays stable via useCallback(fn, []) so useSyncExternalStore never
sees a new subscription function and won't trigger re-render loops.
This is the standard pattern used by zustand and jotai for selector stability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add agentModels and agentRouting to SettingsSchema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add agentRouting module for per-agent provider resolution
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: thread providerOverride through OpenAI shim for per-agent routing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: getAnthropicClient accepts providerOverride for agent routing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: thread providerOverride through Options and queryModel calls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: thread providerOverride through query loop and ToolUseContext
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: resolve agent routing in runAgent and inject providerOverride
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add Agent Routing configuration guide to README
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add unit tests for resolveAgentProvider + plaintext api_key note
- 15 tests covering priority chain (name > subagentType > default > null)
- normalize() case-insensitive and hyphen/underscore equivalence
- Edge cases: null settings, missing config sections, non-existent model
- README note about api_key stored in plaintext
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* security: address code review — SSRF, credential leak, key collision
- base_url schema now uses z.string().url() for SSRF mitigation
- Strip auth headers (Authorization, x-api-key, api-key) from
defaultHeaders when providerOverride is active, preventing
Anthropic credentials from leaking to third-party endpoints
- Warn on duplicate normalized routing keys to prevent silent shadowing
- providerOverride.apiKey is never logged (verified via grep)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: 冯俊辉 <fengjunhui@shiyanjia.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The login picker previously sent third-party users to a dead-end info screen
that only mentioned env vars. This change reuses the existing provider wizard
from the login flow so first-run setup can continue without requiring slash
command access first.
Constraint: The existing provider setup logic must remain the single source of truth
Rejected: Build a separate third-party auth wizard in ConsoleOAuthFlow | would duplicate provider setup behavior and drift over time
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep third-party onboarding routed through ProviderWizard unless the provider command flow is intentionally redesigned
Tested: bun test src/components/ConsoleOAuthFlow.test.tsx src/commands/provider/provider.test.tsx
Tested: tsc --noEmit via project diagnostics
Not-tested: Live gh-authenticated push and PR creation path
Co-authored-by: anandh8x <test@example.com>
Add flickerFreeMode to GlobalConfig so external users can enable
fullscreen alt-screen mode via /config instead of having to set
the CLAUDE_CODE_NO_FLICKER=1 env var manually.
Priority order in isFullscreenEnvEnabled():
CLAUDE_CODE_NO_FLICKER=0 → always off (env wins)
CLAUDE_CODE_NO_FLICKER=1 → always on (env wins)
tmux -CC detected → off (terminal safety guard)
config flickerFreeMode → user preference (new)
USER_TYPE=ant → internal default
The env var still takes full precedence so existing scripts and
automation are unaffected. The new setting only activates when
flickerFreeMode is explicitly set in config.
2026-04-03 21:17:38 +08:00
1084 changed files with 56555 additions and 8684 deletions
* add model caching and benchmarking utilities ([#671](https://github.com/Gitlawb/openclaude/issues/671)) ([2b15e16](https://github.com/Gitlawb/openclaude/commit/2b15e16421f793f954a92c53933a07094544b29d))
* **api:** compress old tool_result content for small-context providers ([#801](https://github.com/Gitlawb/openclaude/issues/801)) ([a6a3de5](https://github.com/Gitlawb/openclaude/commit/a6a3de5ac155fe9d00befbfcab98d439314effd8))
* **api:** improve local provider reliability with readiness and self-healing ([#738](https://github.com/Gitlawb/openclaude/issues/738)) ([4cb963e](https://github.com/Gitlawb/openclaude/commit/4cb963e660dbd6ee438c04042700db05a9d32c59))
* **api:** smart model routing primitive (cheap-for-simple, strong-for-hard) ([#785](https://github.com/Gitlawb/openclaude/issues/785)) ([e908864](https://github.com/Gitlawb/openclaude/commit/e908864da7e7c987a98053ac5d18d702e192db2b))
* enable 15 additional feature flags in open build ([#667](https://github.com/Gitlawb/openclaude/issues/667)) ([6a62e3f](https://github.com/Gitlawb/openclaude/commit/6a62e3ff76ba9ba446b8e20cf2bb139ee76a9387))
* native Anthropic API mode for Claude models on GitHub Copilot ([#579](https://github.com/Gitlawb/openclaude/issues/579)) ([fdef4a1](https://github.com/Gitlawb/openclaude/commit/fdef4a1b4ce218ded4937ca83b30acce7c726472))
* **provider:** expose Atomic Chat in /provider picker with autodetect ([#810](https://github.com/Gitlawb/openclaude/issues/810)) ([ee19159](https://github.com/Gitlawb/openclaude/commit/ee19159c17b3de3b4a8b4a4541a6569f4261d54e))
* **api:** ensure strict role sequence and filter empty assistant messages after interruption ([#745](https://github.com/Gitlawb/openclaude/issues/745) regression) ([#794](https://github.com/Gitlawb/openclaude/issues/794)) ([06e7684](https://github.com/Gitlawb/openclaude/commit/06e7684eb56df8e694ac784575e163641931c44c))
* Collapse all-text arrays to string for DeepSeek compatibility ([#806](https://github.com/Gitlawb/openclaude/issues/806)) ([761924d](https://github.com/Gitlawb/openclaude/commit/761924daa7e225fe8acf41651408c7cae639a511))
* **model:** codex/nvidia-nim/minimax now read OPENAI_MODEL env ([#815](https://github.com/Gitlawb/openclaude/issues/815)) ([4581208](https://github.com/Gitlawb/openclaude/commit/458120889f6ce54cc9f0b287461d5e38eae48a20))
* **provider:** saved profile ignored when stale CLAUDE_CODE_USE_* in shell ([#807](https://github.com/Gitlawb/openclaude/issues/807)) ([13de4e8](https://github.com/Gitlawb/openclaude/commit/13de4e85df7f5fadc8cd15a76076374dc112360b))
* rename .claude.json to .openclaude.json with legacy fallback ([#582](https://github.com/Gitlawb/openclaude/issues/582)) ([4d4fb28](https://github.com/Gitlawb/openclaude/commit/4d4fb2880e4d0e3a62d8715e1ec13d932e736279))
* enforce Bash path constraints after sandbox allow ([#777](https://github.com/Gitlawb/openclaude/issues/777)) ([7002cb3](https://github.com/Gitlawb/openclaude/commit/7002cb302b78ea2a19da3f26226de24e2903fa1d))
* enforce MCP OAuth callback state before errors ([#775](https://github.com/Gitlawb/openclaude/issues/775)) ([739b8d1](https://github.com/Gitlawb/openclaude/commit/739b8d1f40fde0e401a5cbd2b9a55d88bd5124ad))
* require trusted approval for sandbox override ([#778](https://github.com/Gitlawb/openclaude/issues/778)) ([aab4890](https://github.com/Gitlawb/openclaude/commit/aab489055c53dd64369414116fe93226d2656273))
* add OPENCLAUDE_DISABLE_STRICT_TOOLS env var to opt out of strict MCP tool schema normalization ([#770](https://github.com/Gitlawb/openclaude/issues/770)) ([e6e8d9a](https://github.com/Gitlawb/openclaude/commit/e6e8d9a24897e4c9ef08b72df20fabbf8ef27f38))
* mask provider api key input ([#772](https://github.com/Gitlawb/openclaude/issues/772)) ([13e9f22](https://github.com/Gitlawb/openclaude/commit/13e9f22a83a2b0f85f557b1e12c9442ba61241e4))
### Bug Fixes
* allow provider recovery during startup ([#765](https://github.com/Gitlawb/openclaude/issues/765)) ([f828171](https://github.com/Gitlawb/openclaude/commit/f828171ef1ab94e2acf73a28a292799e4e26cc0d))
* **api:** drop orphan tool results to satisfy strict role sequence ([#745](https://github.com/Gitlawb/openclaude/issues/745)) ([b786b76](https://github.com/Gitlawb/openclaude/commit/b786b765f01f392652eaf28ed3579a96b7260a53))
* **mcp:** sync required array with properties in tool schemas ([#754](https://github.com/Gitlawb/openclaude/issues/754)) ([002a8f1](https://github.com/Gitlawb/openclaude/commit/002a8f1f6de2fcfc917165d828501d3047bad61f))
* remove cached mcpClient in diagnostic tracking to prevent stale references ([#727](https://github.com/Gitlawb/openclaude/issues/727)) ([2c98be7](https://github.com/Gitlawb/openclaude/commit/2c98be700274a4241963b5f43530bf3bd8f8963f))
* use raw context window for auto-compact percentage display ([#748](https://github.com/Gitlawb/openclaude/issues/748)) ([55c5f26](https://github.com/Gitlawb/openclaude/commit/55c5f262a9a5a8be0aa9ae8dc6c7dafc465eb2c6))
* add Alibaba Coding Plan (DashScope) provider support ([#509](https://github.com/Gitlawb/openclaude/issues/509)) ([43ac6db](https://github.com/Gitlawb/openclaude/commit/43ac6dba75537282da1e2ad8f855082bc4e25f1e))
* add NVIDIA NIM and MiniMax provider support ([#552](https://github.com/Gitlawb/openclaude/issues/552)) ([51191d6](https://github.com/Gitlawb/openclaude/commit/51191d61326e1f8319d70b3a3c0d9229e185a564))
* add ripgrep to Dockerfile for faster file searching ([#688](https://github.com/Gitlawb/openclaude/issues/688)) ([12dd375](https://github.com/Gitlawb/openclaude/commit/12dd3755c619cc27af3b151ae8fdb9d425a7b9a2))
* **models:** prevent /models crash from non-string saved model values ([#691](https://github.com/Gitlawb/openclaude/issues/691)) ([6b2121d](https://github.com/Gitlawb/openclaude/commit/6b2121da12189fa7ce1f33394d18abd24cf8a01b))
* prevent crash in commands tab when description is undefined ([#730](https://github.com/Gitlawb/openclaude/issues/730)) ([eed77e6](https://github.com/Gitlawb/openclaude/commit/eed77e6579866a98384dcc948a0ad6406614ede3))
* strip comments before scanning for missing imports ([#676](https://github.com/Gitlawb/openclaude/issues/676)) ([a00b792](https://github.com/Gitlawb/openclaude/commit/a00b7928de9662ffb7ef6abd8cd040afe6f4f122))
* **ui:** show correct endpoint URL in intro screen for custom Anthropic endpoints ([#735](https://github.com/Gitlawb/openclaude/issues/735)) ([3424663](https://github.com/Gitlawb/openclaude/commit/34246635fb9a09499047a52e7f96ca9b36c8a85a))
* activate coordinator mode in open build ([#647](https://github.com/Gitlawb/openclaude/issues/647)) ([99a1714](https://github.com/Gitlawb/openclaude/commit/99a17144ee285b892a0801acb6abcc9af68879af))
* activate local-only team memory in open build ([#648](https://github.com/Gitlawb/openclaude/issues/648)) ([24d485f](https://github.com/Gitlawb/openclaude/commit/24d485f42f5b1405d2fab13f2f497d5edd3b5300))
* activate message actions in open build ([#632](https://github.com/Gitlawb/openclaude/issues/632)) ([252808b](https://github.com/Gitlawb/openclaude/commit/252808bbd0a12a6ccf97e2cb09752a0212ea3acd))
* add Docker image build and push to GHCR on release ([#656](https://github.com/Gitlawb/openclaude/issues/656)) ([658d076](https://github.com/Gitlawb/openclaude/commit/658d076909e14eb0459bcb98aee9aa0472118265))
* implement /loop command with fixed and dynamic scheduling ([#621](https://github.com/Gitlawb/openclaude/issues/621)) ([64298a6](https://github.com/Gitlawb/openclaude/commit/64298a663f1391b16aa1f5a49e8a877e1d3742f2))
* implement Monitor tool for streaming shell output ([#649](https://github.com/Gitlawb/openclaude/issues/649)) ([b818dd5](https://github.com/Gitlawb/openclaude/commit/b818dd5958f4e8428566ce25a1a6be5fd4fe66f8))
* local feature flag overrides via ~/.claude/feature-flags.json ([#639](https://github.com/Gitlawb/openclaude/issues/639)) ([0e48884](https://github.com/Gitlawb/openclaude/commit/0e48884f56c6c008f047a7926d3b2cb924170625))
* open useful USER_TYPE-gated features to all users ([#644](https://github.com/Gitlawb/openclaude/issues/644)) ([c1beea9](https://github.com/Gitlawb/openclaude/commit/c1beea98676a413c54152a45a6b9fbe7fb9ed028))
* extend provider guard to protect anthropic profiles from cross-terminal override ([#641](https://github.com/Gitlawb/openclaude/issues/641)) ([03e0b06](https://github.com/Gitlawb/openclaude/commit/03e0b06e0784e4ea46945b3950840b10b6e3ca49))
* improve fetch diagnostics for bootstrap and session requests ([#646](https://github.com/Gitlawb/openclaude/issues/646)) ([df2b9f2](https://github.com/Gitlawb/openclaude/commit/df2b9f2b7b4c661ee3d9ed5dc58b3064de0599d1))
* **openai-shim:** preserve tool result images and local token caps ([#659](https://github.com/Gitlawb/openclaude/issues/659)) ([30c866d](https://github.com/Gitlawb/openclaude/commit/30c866d31ad8538496460667d86ed5efbd4a8547))
* replace broken bun:bundle shim with source pre-processing ([#657](https://github.com/Gitlawb/openclaude/issues/657)) ([adbe391](https://github.com/Gitlawb/openclaude/commit/adbe391e63721918b5d147f4f845111c1a3143db))
* resolve 12 bugs across API, MCP, agent tools, web search, and context overflow ([#674](https://github.com/Gitlawb/openclaude/issues/674)) ([25ce2ca](https://github.com/Gitlawb/openclaude/commit/25ce2ca7bff8937b0b79ad7f85c6dc1c68432069))
* route OpenAI Codex shortcuts to correct endpoint ([#566](https://github.com/Gitlawb/openclaude/issues/566)) ([7c8bdcc](https://github.com/Gitlawb/openclaude/commit/7c8bdcc3e2ac1ecb98286c705c85671044be3d6b))
* **read/edit:** make compact line prefix unambiguous for tab-indented files ([#613](https://github.com/Gitlawb/openclaude/issues/613)) ([08cc6f3](https://github.com/Gitlawb/openclaude/commit/08cc6f328711cd93ce9fa53351266c29a0b0a341))
* add auto-fix service — auto-lint and test after AI file edits ([#508](https://github.com/Gitlawb/openclaude/issues/508)) ([c385047](https://github.com/Gitlawb/openclaude/commit/c385047abba4366866f4c87bfb5e0b0bd4dcbb9d))
* Add Gemini support with thought_signature fix ([#404](https://github.com/Gitlawb/openclaude/issues/404)) ([5012c16](https://github.com/Gitlawb/openclaude/commit/5012c160c9a2dff9418e7ee19dc9a4d29ef2b024))
* add headless gRPC server for external agent integration ([#278](https://github.com/Gitlawb/openclaude/issues/278)) ([26eef92](https://github.com/Gitlawb/openclaude/commit/26eef92fe72e9c3958d61435b8d3571e12bf2b74))
* add wiki mvp commands ([#532](https://github.com/Gitlawb/openclaude/issues/532)) ([c328fdf](https://github.com/Gitlawb/openclaude/commit/c328fdf9e2fe59ad101b049301298ce9ff24caca))
* GitHub provider lifecycle and onboarding hardening ([#351](https://github.com/Gitlawb/openclaude/issues/351)) ([ff7d499](https://github.com/Gitlawb/openclaude/commit/ff7d49990de515825ddbe4099f3a39b944b61370))
### Bug Fixes
* add File polyfill for Node < 20 to prevent startup deadlock with proxy ([#442](https://github.com/Gitlawb/openclaude/issues/442)) ([85aa8b0](https://github.com/Gitlawb/openclaude/commit/85aa8b0985c8f3cb8801efa5141114a0ab0f6a83))
* add GitHub Copilot model context windows and output limits ([#576](https://github.com/Gitlawb/openclaude/issues/576)) ([a7f5982](https://github.com/Gitlawb/openclaude/commit/a7f5982f6438ab0ddc3f0daae31ea68ac7ac206c)), closes [#515](https://github.com/Gitlawb/openclaude/issues/515)
* add LiteLLM-style aliases for GitHub Copilot context windows ([#606](https://github.com/Gitlawb/openclaude/issues/606)) ([2e0e14d](https://github.com/Gitlawb/openclaude/commit/2e0e14d71313e0e501efaa9e55c6c56f2742fb10))
* add store:false to Chat Completions and /responses fallback ([#578](https://github.com/Gitlawb/openclaude/issues/578)) ([8aaa4f2](https://github.com/Gitlawb/openclaude/commit/8aaa4f22ac5b942d82aa9cad54af30d56034515a))
* convert dragged file paths to [@mentions](https://github.com/mentions) for attachment ([#382](https://github.com/Gitlawb/openclaude/issues/382)) ([112df59](https://github.com/Gitlawb/openclaude/commit/112df5911791ea71ee9efbb98ea59c5ded1ea161))
* custom web search — WEB_URL_TEMPLATE not recognized, timeout too short, silent native fallback ([#537](https://github.com/Gitlawb/openclaude/issues/537)) ([32fbd0c](https://github.com/Gitlawb/openclaude/commit/32fbd0c7b4168b32dcb13a5b69342e2727269201))
* defer startup checks and suppress recommendation dialogs during startup window (issue [#363](https://github.com/Gitlawb/openclaude/issues/363)) ([#504](https://github.com/Gitlawb/openclaude/issues/504)) ([2caf2fd](https://github.com/Gitlawb/openclaude/commit/2caf2fd982af1ec845c50152ad9d28d1a597f82f))
* display selected model in startup screen instead of hardcoded sonnet 4.6 ([#587](https://github.com/Gitlawb/openclaude/issues/587)) ([b126e38](https://github.com/Gitlawb/openclaude/commit/b126e38b1affddd2de83fcc3ba26f2e44b42a509))
* handle missing skill parameter in SkillTool ([#485](https://github.com/Gitlawb/openclaude/issues/485)) ([f9ce81b](https://github.com/Gitlawb/openclaude/commit/f9ce81bfb384e909353813fb6f6760cadd508ae7))
* include MCP tool results in microcompact to reduce token waste ([#348](https://github.com/Gitlawb/openclaude/issues/348)) ([52d33a8](https://github.com/Gitlawb/openclaude/commit/52d33a87a047b943aedaaaf772cd48636c263509))
* let saved provider profiles win on restart ([#513](https://github.com/Gitlawb/openclaude/issues/513)) ([cb8f8b7](https://github.com/Gitlawb/openclaude/commit/cb8f8b7ac2e3e74516ee219a3a48156db7c6ed78))
* preserve only originally-required properties in strict tool schemas ([#471](https://github.com/Gitlawb/openclaude/issues/471)) ([ccaa193](https://github.com/Gitlawb/openclaude/commit/ccaa193eec5761f0972ffb58eb3189a81a9244b0))
* preserve unicode in Windows clipboard fallback ([#388](https://github.com/Gitlawb/openclaude/issues/388)) ([c193497](https://github.com/Gitlawb/openclaude/commit/c1934974aaf64db460cc850a044bd13cc744cce7))
* rebrand prompt identity to openclaude ([#496](https://github.com/Gitlawb/openclaude/issues/496)) ([598651f](https://github.com/Gitlawb/openclaude/commit/598651f42389ce76311ec00e8a9c701c939ead27))
* strip Anthropic params from 3P resume paths ([#479](https://github.com/Gitlawb/openclaude/issues/479)) ([4975cfc](https://github.com/Gitlawb/openclaude/commit/4975cfc2e0ddbe34aa4e8e3f52ee5eba07fbe465))
* suppress startup dialogs when input is buffered ([#423](https://github.com/Gitlawb/openclaude/issues/423)) ([8ece290](https://github.com/Gitlawb/openclaude/commit/8ece2900872dadd157e798ef501ddf126dac66c4))
* **tui:** restore prompt rendering on startup ([#498](https://github.com/Gitlawb/openclaude/issues/498)) ([e30ad17](https://github.com/Gitlawb/openclaude/commit/e30ad17ae0056787273be2caafd6cf5340b6ab57))
* update theme preview on focus change ([#562](https://github.com/Gitlawb/openclaude/issues/562)) ([6924718](https://github.com/Gitlawb/openclaude/commit/692471850fc789ee0797190089272407f9a4d953))
* **web-search:** close SSRF bypasses in custom provider hostname guard ([#610](https://github.com/Gitlawb/openclaude/issues/610)) ([a02c441](https://github.com/Gitlawb/openclaude/commit/a02c44143b257fbee7f38f1b93873cc0ea68a1f9))
OpenClaude is a fast-moving open-source coding-agent CLI with support for multiple providers, local backends, MCP, and a terminal-first workflow. The best contributions here are focused, well-tested, and easy to review.
## Before You Start
- Search existing [issues](https://github.com/Gitlawb/openclaude/issues) and [discussions](https://github.com/Gitlawb/openclaude/discussions) before opening a new thread.
- Use issues for confirmed bugs and actionable feature work.
- Use discussions for setup help, ideas, and general community conversation.
- For larger changes, open an issue first so the scope is clear before implementation.
- For security reports, follow [SECURITY.md](SECURITY.md).
## Local Setup
Install dependencies:
```bash
bun install
```
Build the CLI:
```bash
bun run build
```
Smoke test:
```bash
bun run smoke
```
Run the app locally:
```bash
bun run dev
```
If you are working on provider setup or saved profiles, useful commands include:
```bash
bun run profile:init
bun run dev:profile
```
## Development Workflow
- Keep PRs focused on one problem or feature.
- Avoid mixing unrelated cleanup into the same change.
- Preserve existing repo patterns unless the change is intentionally refactoring them.
- Add or update tests when the change affects behavior.
- Update docs when setup, commands, or user-facing behavior changes.
## Validation
At minimum, run the most relevant checks for your change.
Common checks:
```bash
bun run build
bun run smoke
```
Focused tests:
```bash
bun test ./path/to/test-file.test.ts
```
When working on provider/runtime setup, this can also help:
```bash
bun run doctor:runtime
```
## Pull Requests
Good PRs usually include:
- a short explanation of what changed
- why it changed
- the user or developer impact
- the exact checks you ran
If the PR touches UI, terminal presentation, or the VS Code extension, include screenshots when useful.
If the PR changes provider behavior, mention which provider path was tested.
## Code Style
- Follow the existing code style in the touched files.
- Prefer small, readable changes over broad rewrites.
- Do not reformat unrelated files just because they are nearby.
- Keep comments useful and concise.
## Provider Changes
OpenClaude supports multiple provider paths. If you change provider logic:
- be explicit about which providers are affected
- avoid breaking third-party providers while fixing first-party behavior
- test the exact provider/model path you changed when possible
- call out any limitations or follow-up work in the PR description
## Community
Please be respectful and constructive with other contributors.
Maintainers may ask for:
- narrower scope
- focused follow-up PRs
- stronger validation
- docs updates for behavior changes
That is normal and helps keep the project reviewable as it grows.
OpenClaude is an open-source coding-agent CLI that works with more than one model provider.
OpenClaude is an open-source coding-agent CLI for cloud and local model providers.
Use OpenAI-compatible APIs, Gemini, GitHub Models, Codex, Ollama, Atomic Chat, and other supported backends while keeping the same terminal-first workflow: prompts, tools, agents, MCP, slash commands, and streaming output.
Use OpenAI-compatible APIs, Gemini, GitHub Models, Codex OAuth, Codex, Ollama, Atomic Chat, and other supported backends while keeping one terminal-first workflow: prompts, tools, agents, MCP, slash commands, and streaming output.
[](https://www.star-history.com/?repos=gitlawb%2Fopenclaude&type=date&legend=top-left)
## Why OpenClaude
- Use one CLI across cloud and local model providers
- Use one CLI across cloud APIs and local model backends
- Save provider profiles inside the app with `/provider`
- Run locally with Ollama or Atomic Chat
- Keep core coding-agent workflows: bash, file tools, grep, glob, agents, tasks, MCP, and web tools
---
- Run with OpenAI-compatible services, Gemini, GitHub Models, Codex OAuth, Codex, Ollama, Atomic Chat, and other supported providers
- Keep coding-agent workflows in one place: bash, file tools, grep, glob, agents, tasks, MCP, and web tools
- Use the bundled VS Code extension for launch integration and theme support
## Quick Start
@@ -21,7 +35,7 @@ Use OpenAI-compatible APIs, Gemini, GitHub Models, Codex, Ollama, Atomic Chat, a
npm install -g @gitlawb/openclaude
```
If the npm install path later reports `ripgrep not found`, install ripgrep system-wide and confirm `rg --version` works in the same terminal before starting OpenClaude.
If the install later reports `ripgrep not found`, install ripgrep system-wide and confirm `rg --version` works in the same terminal before starting OpenClaude.
### Start
@@ -31,8 +45,8 @@ openclaude
Inside OpenClaude:
- run `/provider` for guided setup of OpenAI-compatible, Gemini, Ollama, or Codex profiles
- run `/onboard-github` for GitHub Models setup
- run `/provider` for guided provider setup and saved profiles
- run `/onboard-github` for GitHub Models onboarding
If you have [Ollama](https://ollama.com) installed, you can skip the env var setup entirely:
```bash
ollama launch openclaude --model qwen2.5-coder:7b
```
This automatically sets `ANTHROPIC_BASE_URL`, model routing, and auth so all API traffic goes through your local Ollama instance. Works with any model you have pulled — local or cloud.
## Setup Guides
@@ -93,38 +115,27 @@ Advanced and source-build guides:
- [Advanced Setup](docs/advanced-setup.md)
- [Android Install](ANDROID_INSTALL.md)
---
## Supported Providers
| Provider | Setup Path | Notes |
| --- | --- | --- |
| OpenAI-compatible | `/provider` or env vars | Works with OpenAI, OpenRouter, DeepSeek, Groq, Mistral, LM Studio, and compatible local `/v1` servers |
| Gemini | `/provider` or env vars | Google Gemini support through the runtime provider layer |
| OpenAI-compatible | `/provider` or env vars | Works with OpenAI, OpenRouter, DeepSeek, Groq, Mistral, LM Studio, and other compatible `/v1` servers |
| Gemini | `/provider` or env vars | Supports API key, access token, or local ADC workflow on current `main` |
- **Streaming responses**: Real-time token output and tool progress
-**Tool calling**: Multi-step tool loops with model calls, tool execution, and follow-up responses
- **Images**: URL and base64 image inputs for providers that support vision
-**Provider profiles**: Guided setup plus saved `.openclaude-profile.json` support
- **Local and remote model backends**: Cloud APIs, local servers, and Apple Silicon local inference
## Provider Notes
@@ -137,13 +148,49 @@ OpenClaude supports multiple providers, but behavior is not identical across all
For best results, use models with strong tool/function calling support.
---
## Agent Routing
OpenClaude can route different agents to different models through settings-based routing. This is useful for cost optimization or splitting work by model strength.
Add to `~/.claude/settings.json`:
```json
{
"agentModels":{
"deepseek-chat":{
"base_url":"https://api.deepseek.com/v1",
"api_key":"sk-your-key"
},
"gpt-4o":{
"base_url":"https://api.openai.com/v1",
"api_key":"sk-your-key"
}
},
"agentRouting":{
"Explore":"deepseek-chat",
"Plan":"gpt-4o",
"general-purpose":"gpt-4o",
"frontend-dev":"deepseek-chat",
"default":"gpt-4o"
}
}
```
When no routing match is found, the global provider remains the fallback.
> **Note:** `api_key` values in `settings.json` are stored in plaintext. Keep this file private and do not commit it to version control.
## Web Search and Fetch
`WebFetch` works out of the box.
By default, `WebSearch` works on non-Anthropic models using DuckDuckGo. This gives GPT-4o, DeepSeek, Gemini, Ollama, and other OpenAI-compatible providers a free web search path out of the box.
`WebSearch` and richer JS-aware fetching work best with a Firecrawl API key:
> **Note:** DuckDuckGo fallback works by scraping search results and may be rate-limited, blocked, or subject to DuckDuckGo's Terms of Service. If you want a more reliable supported option, configure Firecrawl.
For Anthropic-native backends and Codex responses, OpenClaude keeps the native provider web search behavior.
`WebFetch` works, but its basic HTTP plus HTML-to-markdown path can still fail on JavaScript-rendered sites or sites that block plain HTTP requests.
Set a [Firecrawl](https://firecrawl.dev) API key if you want Firecrawl-powered search/fetch behavior:
-`WebSearch`is available across more provider setups
-`WebFetch`can handle JavaScript-rendered pages more reliably
-`WebSearch`can use Firecrawl's search API while DuckDuckGo remains the default free path for non-Claude models
-`WebFetch`uses Firecrawl's scrape endpoint instead of raw HTTP, handling JS-rendered pages correctly
Firecrawl is optional. Without it, OpenClaude falls back to the built-in behavior.
Free tier at [firecrawl.dev](https://firecrawl.dev) includes 500 credits. The key is optional.
---
## Source Build
## Headless gRPC Server
OpenClaude can be run as a headless gRPC service, allowing you to integrate its agentic capabilities (tools, bash, file editing) into other applications, CI/CD pipelines, or custom user interfaces. The server uses bidirectional streaming to send real-time text chunks, tool calls, and request permissions for sensitive commands.
### 1. Start the gRPC Server
Start the core engine as a gRPC service on `localhost:50051`:
| `GRPC_PORT` | `50051` | Port the gRPC server listens on |
| `GRPC_HOST` | `localhost` | Bind address. Use `0.0.0.0` to expose on all interfaces (not recommended without authentication) |
### 2. Run the Test CLI Client
We provide a lightweight CLI client that communicates exclusively over gRPC. It acts just like the main interactive CLI, rendering colors, streaming tokens, and prompting you for tool permissions (y/n) via the gRPC `action_required` event.
In a separate terminal, run:
```bash
npm run dev:grpc:cli
```
*Note: The gRPC definitions are located in `src/proto/openclaude.proto`. You can use this file to generate clients in Python, Go, Rust, or any other language.*
---
## Source Build And Local Development
```bash
bun install
@@ -169,22 +249,78 @@ node dist/cli.mjs
Helpful commands:
-`bun run dev`
-`bun test`
-`bun run test:coverage`
-`bun run security:pr-scan -- --base origin/main`
-`bun run smoke`
-`bun run doctor:runtime`
-`bun run verify:privacy`
- focused `bun test ...` runs for the areas you touch
---
## Testing And Coverage
OpenClaude uses Bun's built-in test runner for unit tests.
Run the full unit suite:
```bash
bun test
```
Generate unit test coverage:
```bash
bun run test:coverage
```
Open the visual coverage report:
```bash
open coverage/index.html
```
If you already have `coverage/lcov.info` and only want to rebuild the UI:
```bash
bun run test:coverage:ui
```
Use focused test runs when you only touch one area:
-`bun run test:provider`
-`bun run test:provider-recommendation`
-`bun test path/to/file.test.ts`
Recommended contributor validation before opening a PR:
-`bun run build`
-`bun run smoke`
-`bun run test:coverage` for broader unit coverage when your change affects shared runtime or provider logic
- focused `bun test ...` runs for the files and flows you changed
Coverage output is written to `coverage/lcov.info`, and OpenClaude also generates a git-activity-style heatmap at `coverage/index.html`.
## Repository Structure
-`src/` - core CLI/runtime
-`scripts/` - build, verification, and maintenance scripts
-`docs/` - setup, contributor, and project documentation
-`python/` - standalone Python helpers and their tests
-`vscode-extension/openclaude-vscode/` - VS Code extension
-`.github/` - repo automation, templates, and CI configuration
-`bin/` - CLI launcher entrypoints
## VS Code Extension
The repo includes a VS Code extension in [`vscode-extension/openclaude-vscode`](vscode-extension/openclaude-vscode) for OpenClaude launch integration and theme support.
---
The repo includes a VS Code extension in [`vscode-extension/openclaude-vscode`](vscode-extension/openclaude-vscode) for OpenClaude launch integration, provider-aware control-center UI, and theme support.
## Security
If you believe you found a security issue, see [SECURITY.md](SECURITY.md).
---
## Community
- Use [GitHub Discussions](https://github.com/Gitlawb/openclaude/discussions) for Q&A, ideas, and community conversation
- Use [GitHub Issues](https://github.com/Gitlawb/openclaude/issues) for confirmed bugs and actionable feature work
## Contributing
@@ -193,19 +329,17 @@ Contributions are welcome.
For larger changes, open an issue first so the scope is clear before implementation. Helpful validation commands include:
-`bun run build`
-`bun run test:coverage`
-`bun run smoke`
- focused `bun test ...` runs for touched areas
- focused `bun test ...` runs for files and flows youchanged
---
## Disclaimer
OpenClaude is an independent community project and is not affiliated with, endorsed by, or sponsored by Anthropic.
"Claude" and "Claude Code" are trademarks of Anthropic.
---
OpenClaude originated from the Claude Code codebase and has since been substantially modified to support multiple providers and open use. "Claude" and "Claude Code" are trademarks of Anthropic PBC. See [LICENSE](LICENSE) for details.
`codexplan` maps to GPT-5.4 on the Codex backend with high reasoning.
`codexspark` maps to GPT-5.3 Codex Spark for faster loops.
If you use the in-app provider wizard, choose `Codex OAuth` to open ChatGPT sign-in in your browser and let OpenClaude store Codex credentials securely.
If you already use the Codex CLI, OpenClaude reads `~/.codex/auth.json` automatically. You can also point it elsewhere with `CODEX_AUTH_JSON_PATH` or override the token directly with `CODEX_API_KEY`.
```bash
@@ -82,6 +84,16 @@ OpenRouter model availability changes over time. If a model stops working, try a
### Ollama
Using `ollama launch` (recommended if you have Ollama installed):
```bash
ollama launch openclaude --model llama3.3:70b
```
This handles all environment setup automatically — no env vars needed. Works with any local or cloud model available in your Ollama instance.
-`feature('HOOK_CHAINS')` must be enabled in the build
-`CLAUDE_CODE_ENABLE_HOOK_CHAINS=0|1` (defaults to disabled when unset)
## Safety Guarantees
The runtime is intentionally conservative:
- **Depth guard:** chain dispatch is blocked when `chainDepth >= maxChainDepth`.
- **Rule cooldown:** each rule can only re-fire after cooldown expires.
- **Dedup window:** identical event/action combinations are suppressed for a window.
- **Abort-safe behavior:** if the current signal is aborted, actions skip safely.
- **Policy-aware remote warm:** `warm_remote_capacity` skips when remote sessions are policy denied.
- **Bridge inactive no-op:** `warm_remote_capacity` safely skips when no active bridge handle exists.
- **Missing team context safety:** `notify_team` skips with structured reason if no team context/team file is available.
- **Fallback launcher safety:** `spawn_fallback_agent` fails with a structured reason when launch permissions/context are unavailable.
## Configuration Schema Reference
Top-level object:
```json
{
"version":1,
"enabled":true,
"maxChainDepth":2,
"defaultCooldownMs":30000,
"defaultDedupWindowMs":30000,
"rules":[]
}
```
### Top-Level Fields
| Field | Type | Required | Notes |
|---|---|---:|---|
| `version` | `1` | No | Defaults to `1`. |
| `enabled` | `boolean` | No | Global feature switch for this config file. |
| `maxChainDepth` | `integer` | No | Global depth guard (default `2`, max `10`). |
| `defaultCooldownMs` | `integer` | No | Default rule cooldown in ms (default `30000`). |
| `defaultDedupWindowMs` | `integer` | No | Default action dedup window in ms (default `30000`). |
| `rules` | `HookChainRule[]` | No | Defaults to `[]`. May be omitted or empty; when no rules are present, dispatch is a no-op and returns `enabled: false`. |
> **Note:** An empty ruleset is valid and can be used to keep Hook Chains configured but effectively disabled until rules are added.
### Rule Object (`HookChainRule`)
```json
{
"id":"task-failure-recovery",
"enabled":true,
"trigger":{
"event":"TaskCompleted",
"outcome":"failed"
},
"condition":{
"toolNames":["Edit"],
"taskStatuses":["failed"],
"errorIncludes":["timeout","permission denied"],
"eventFieldEquals":{
"meta.source":"scheduler"
}
},
"cooldownMs":60000,
"dedupWindowMs":30000,
"maxDepth":2,
"actions":[]
}
```
| Field | Type | Required | Notes |
|---|---|---:|---|
| `id` | `string` | Yes | Stable identifier used in telemetry/guards. |
| `enabled` | `boolean` | No | Per-rule switch. |
| `trigger.event` | `HookEvent` | Yes | Event name to match. |
| `trigger.outcome` | `"success"|"failed"|"timeout"|"unknown"` | No | Single outcome matcher. |
| `trigger.outcomes` | `Outcome[]` | No | Multi-outcome matcher. Use either `outcome` or `outcomes`. |
| `condition` | `object` | No | Optional extra matching constraints. |
| `cooldownMs` | `integer` | No | Overrides global cooldown for this rule. |
| `dedupWindowMs` | `integer` | No | Overrides global dedup for this rule. |
OpenClaude can connect to LiteLLM through LiteLLM's OpenAI-compatible proxy.
## Overview
LiteLLM is an open-source LLM gateway that provides a unified API to 100+ model providers. By running the LiteLLM Proxy, you can route OpenClaude requests through LiteLLM to access any of its supported providers — all while using OpenClaude's existing OpenAI-compatible provider path.
-`OPENAI_MODEL` must match the **LiteLLM model alias** defined in your config, not the upstream raw provider model name.
- If your proxy requires authentication, use the proxy key (or `master_key`) in `OPENAI_API_KEY`.
- LiteLLM's OpenAI-compatible endpoint accepts the same request format as OpenAI, so OpenClaude works without any code changes.
- You can switch between any provider configured in LiteLLM by simply changing the `OPENAI_MODEL` value — no need to reconfigure OpenClaude.
## 5. Troubleshooting
| Issue | Likely Cause | Fix |
|-------|--------------|-----|
| 404 or Model Not Found | Model alias doesn't exist in LiteLLM config | Verify the `model_name` in `litellm_config.yaml` matches `OPENAI_MODEL` |
| Connection Refused | LiteLLM proxy isn't running | Start the proxy with `litellm --config litellm_config.yaml --port 4000` |
| Auth Failed | Missing or wrong `master_key` | Set the correct key in `OPENAI_API_KEY` |
| Upstream provider error | The backend provider key is missing or invalid | Ensure the upstream API key (e.g., `OPENAI_API_KEY`) is set in your LiteLLM proxy process environment |
| Tools fail but chat works | The selected model has weak function/tool calling support | Switch to a model with strong tool support (e.g., GPT-4o, Claude Sonnet) |
"description":"Claude Code opened to any LLM — OpenAI, Gemini, DeepSeek, Ollama, and 200+ models",
"type":"module",
"bin":{
@@ -30,10 +30,18 @@
"profile:code":"bun run profile:init -- --provider ollama --model qwen2.5-coder:7b",
"dev:fast":"bun run profile:fast && bun run dev:ollama:fast",
"dev:code":"bun run profile:code && bun run dev:profile",
"dev:grpc":"bun run scripts/start-grpc.ts",
"dev:grpc:cli":"bun run scripts/grpc-cli.ts",
"start":"node dist/cli.mjs",
"test":"bun test",
"test:coverage":"bun test --coverage --coverage-reporter=lcov --coverage-dir=coverage --max-concurrency=1 && bun run scripts/render-coverage-heatmap.ts",
"test:coverage:ui":"bun run scripts/render-coverage-heatmap.ts",
"security:pr-scan":"bun run scripts/pr-intent-scan.ts",
"test:provider-recommendation":"bun test src/utils/providerRecommendation.test.ts src/utils/providerProfile.test.ts",
"typecheck":"tsc --noEmit",
"smoke":"bun run build && node dist/cli.mjs --version",
"verify:privacy":"bun run scripts/verify-no-phone-home.ts",
"build:verified":"bun run build && bun run verify:privacy",
"test:provider":"bun test src/services/api/*.test.ts src/utils/context.test.ts",
"doctor:runtime":"bun run scripts/system-check.ts",
"doctor:runtime:json":"bun run scripts/system-check.ts --json",
return`${base}${bodySuffix} Hint: model alias "${request.requestedModel}" resolved to "${request.resolvedModel}", which this ChatGPT account does not currently allow. Try "codexplan" or another entitled Codex model.`
}
functioncheckNodeVersion():CheckResult{
constraw=process.versions.node
constmajor=Number(raw.split('.')[0]??'0')
@@ -93,14 +123,18 @@ function isLocalBaseUrl(baseUrl: string): boolean {
@@ -4592,7 +4582,7 @@ function handleSetPermissionMode(
subtype:'error',
request_id: requestId,
error:
'Cannot set permission mode to bypassPermissions because the session was not launched with --dangerously-skip-permissions',
'Cannot set permission mode to bypassPermissions. Enable it with --allow-dangerously-skip-permissions or set permissions.allowBypassPermissionsMode in settings.json',
},
})
returntoolPermissionContext
@@ -5025,7 +5015,7 @@ async function loadInitialMessages(
}
// Handle resume in print mode (accepts session ID or URL)
description:'Configure auto-fix: run lint/test after AI edits',
isEnabled:()=>true,
type:'prompt',
progressMessage:'Configuring auto-fix...',
contentLength: 0,
source:'builtin',
asyncgetPromptForCommand() {
return[
{
type:'text',
text:
'The user wants to configure auto-fix settings. Auto-fix automatically runs lint and test commands after AI file edits, feeding errors back for self-repair.\n\n'+
'Current settings location: `.claude/settings.json` or `.claude/settings.local.json`\n\n'+
// Large system prompt (~6000 chars, ~1500 tokens) to cross the 1024-token cache threshold
constSYSTEM_PROMPT=[
'You are a coding assistant. Answer concisely.',
'CONTEXT: User is working on a TypeScript project with Bun runtime.',
...Array.from(
{length: 80},
(_,i)=>
`Rule ${i+1}: Follow best practices for TypeScript including strict typing, error handling, testing, and clean code. Prefer explicit types over any. Use const assertions. Await all async operations.`,
),
].join('\n\n')
constUSER_MESSAGE='Say "hello" and nothing else.'
constDELAY_MS=3000
/**
* Extract model family from a versioned model string.
* e.g. "gpt-5.4-0626" → "gpt-5.4", "codex-mini-latest" → "codex-mini"
5. After creating/updating the PR, check if the user's CLAUDE.md mentions posting to Slack channels. If it does, use ToolSearch to search for "slack send message" tools. If ToolSearch finds a Slack tool, ask the user if they'd like you to post the PR URL to the relevant Slack channel. Only post if the user confirms. If ToolSearch returns no results or errors, skip this step silently—do not mention the failure, do not attempt workarounds, and do not try alternative approaches.`
5. After creating/updating the PR, check if the user's AGENTS.md or CLAUDE.md mentions posting to Slack channels. If it does, use ToolSearch to search for "slack send message" tools. If ToolSearch finds a Slack tool, ask the user if they'd like you to post the PR URL to the relevant Slack channel. Only post if the user confirms. If ToolSearch returns no results or errors, skip this step silently—do not mention the failure, do not attempt workarounds, and do not try alternative approaches.`
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.