* feat(config): support multiple API keys for failover
Add api_keys field to ModelConfig to support multiple API keys with
automatic failover. When multiple keys are configured, they are expanded
into separate model entries with fallbacks set up for key-level failover.
Example config:
{
"model_name": "glm-4.7",
"model": "zhipu/glm-4.7",
"api_keys": ["key1", "key2", "key3"]
}
Expands internally to:
- glm-4.7 (key1) -> fallbacks: [glm-4.7__key_1, glm-4.7__key_2]
- glm-4.7__key_1 (key2)
- glm-4.7__key_2 (key3)
Backward compatible: single api_key still works as before.
* fix(providers): change cooldown tracking from provider to ModelKey
This enables proper key-switching when multiple API keys share the same
provider. Previously, when one key failed, all keys were blocked because
cooldown was tracked per-provider.
Now each (provider, model) combination has independent cooldown, allowing
fallback to alternate keys when one is rate limited.
Includes TestMultiKeyWithModelFallback and related failover tests.
When building parameters for Anthropic API calls, tool calls with empty
names would cause 400 Bad Request errors with the message:
'tool_use.name: String should have at least 1 character'
This fix adds a check to skip tool calls that have empty names, preventing
the API error and allowing the conversation to continue normally.
Fixes#1658
* Add Novita provider support
- Add 'novita' prefix to normalizeModel switch in openai_compat provider
- Add Novita provider to all_supported_vendors table in README.md
- Add test cases for Novita model prefix stripping
Novita endpoint: https://api.novita.ai/openai
Default models: deepseek/deepseek-v3.2, zai-org/glm-5, minimax/minimax-m2.5
* feat: complete Novita provider integration
* chore: drop README changes from Novita PR
* fix: remove duplicate function declarations in openai_compat provider
The functions buildToolsList, SupportsNativeSearch, and isNativeSearchHost
were declared twice, causing compilation failures in all CI checks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: break long line in novita test to satisfy golines linter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Problem:
During subturn context limit or truncation recoveries, the recovery loops repeatedly
called `runAgentLoop` with the same or modified `UserMessage`. Because `runAgentLoop`
unconditionally adds the `UserMessage` to the session history, this resulted in:
1. Duplicate User Messages polluting the history upon `context_length_exceeded` retries.
2. The possibility of injecting empty User Messages if `opts.UserMessage` was artificially blanked out to work around the duplication.
3. Messy or duplicate entries during `finish_reason="truncated"` recovery injections.
Solution:
- Introduce `SkipAddUserMessage` boolean to `processOptions` to explicitly control whether the agent loop should write the user prompt to history.
- Add an explicit `opts.UserMessage != ""` check in `runAgentLoop` to prevent polluting history with empty message content.
- In `subturn.go`'s recovery loop, set `SkipAddUserMessage: contextRetryCount > 0` to skip writing the user message on context
* config: add prefer_native and NativeSearchCapable for model-native search
* providers: implement native web search for OpenAI and Codex
* agent: use provider-native search when prefer_native and supported
* tests: add coverage for model-native search
* fix: Golang lint errors
* fix: update the code based on the review
* fix: update codex_provider_test
- add defensive nil check for tool call Arguments field
- replace nil input with empty object to comply with Anthropic spec
- prevent API errors when GLM models return null input in tool_use blocks
Zhipu AI's GLM series models may return tool_use blocks with null input field,
which causes their API to reject subsequent requests with error:
"ClaudeContentBlockToolResult object has no attribute id"
This fix ensures compatibility by converting nil inputs to empty objects {},
matching the Anthropic Messages API specification while maintaining backward
compatibility with other providers.
* Add support for azure openai provider
* Add checks for deployment model name
* Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Addressing @Copilot suggestion to remove the init() function which seemed redundant
* Fix readme
* Fix linting checks
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* feat: add anthropic-messages protocol support
Add native Anthropic Messages API format support to enable
compatibility with custom endpoints that only support Anthropic's
native message format (not OpenAI-compatible format).
Changes:
- Add new pkg/providers/anthropic_messages package with HTTP-based provider
- Implement Anthropic Messages API request/response format conversion
- Add anthropic-messages protocol support in factory_provider.go
- Include comprehensive unit tests (64.2% coverage)
Features:
- Support for system, user, assistant, and tool messages
- Support for tool calls (tool_use blocks)
- Proper header handling (x-api-key, anthropic-version)
- Configurable max_tokens and temperature
- Automatic base URL normalization
Configuration example:
model: "anthropic-messages/claude-opus-4-6"
api_base: "https://api.anthropic.com"
api_key: "sk-..."
Tested with actual API endpoint, verified compatibility
with Anthropic Messages API specification.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: add anthropic-messages protocol examples to README and config
Add configuration examples and documentation for the new
anthropic-messages protocol:
- config.example.json: Add claude-opus-4.6 example with anthropic-messages
- README.md: Add "Anthropic Messages API (native format)" section
- README.zh.md: Add Chinese version of the documentation
This helps users understand when to use anthropic-messages vs
anthropic protocol and fixes issue #269.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: format code with gofmt -s
- Align constant definitions in provider.go
- Align struct fields in test cases
- Fix gofmt formatting issues reported in review
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: address linter errors
- Fix HTTP header canonical form: "x-api-key" → "X-API-Key"
- Fix HTTP header canonical form: "anthropic-version" → "Anthropic-Version"
- Format imports with gci (standard, default, localmodule order)
- Format code with golines (max line length 120)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: resolve golangci-lint errors in anthropic-messages provider
- add nolint comment for canonicalheader rule on X-API-Key header (Anthropic API requires exact casing)
- fix golines formatting issues in provider_test.go (split long lines under 120 chars)
- fix long comment line in factory_provider.go (split into two lines)
Resolves CI linter failures for the anthropic-messages protocol implementation.
* fix(providers): address review comments in anthropic-messages provider
- fix normalizeBaseURL edge case that incorrectly appends /v1 to URLs already containing /v1 path (e.g., https://api.example.com/v1/proxy)
- remove dead code for apiBase empty check as normalizeBaseURL() always provides a default value
- update test to use proper constructor instead of direct struct initialization
- add detailed comments explaining the URL normalization logic
Resolves review comments on PR #1284
* fix(providers): remove hardcoded max_tokens in anthropic-messages provider
- remove hardcoded max_tokens value (4096) from buildRequestBody
- read max_tokens directly from options parameter
- add error handling when max_tokens is missing from options
- update test cases to include max_tokens in options
This fix ensures the provider respects the config default value (32768)
or system fallback (8192) instead of always using the hardcoded 4096.
* fix(providers): improve error handling and add edge case tests
- fix ToolCalls nil vs empty slice issue to ensure consistent JSON serialization
- add detailed HTTP error handling for common status codes (401, 429, 400, 404, 500, 503)
- add edge case tests for buildRequestBody and parseResponseBody
- clarify anthropic vs anthropic-messages protocol differences in docs
---------
Co-authored-by: Claude <noreply@anthropic.com>
When the claude CLI exits with a non-zero status, the previous error
handler only checked stderr. However, the CLI writes its output
(including error details) to stdout, especially when invoked with
--output-format json. This left the caller with only "exit status 1"
and no actionable information.
Now includes both stderr and stdout in the error message so the actual
failure reason is visible in logs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: swap header logo to webp, move meme logo to bottom
Replace header logo with assets/logo.webp across all 6 README
language variants and move the original meme logo (logo.jpg)
to the bottom of each file.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: update GPT model names to gpt-5.4 and refine provider descriptions
Update all 6 language README variants:
- Correct GPT model references from gpt-5.2/gpt4 to gpt-5.4
- Refine provider descriptions in API Key comparison tables
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update default model to gpt-5.4, codex to gpt-5.3-codex
Update OpenAI default model references from gpt-5.2 to gpt-5.4
across source code, config examples, tests, and docs. Set Codex
default model to gpt-5.3-codex.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(providers): add LongCat model provider support
Add LongCat as an OpenAI-compatible provider with base URL
https://api.longcat.chat/openai and default model LongCat-Flash-Thinking.
Includes provider config, migration, factory routing, example config,
tests, and README entries for all 6 locales.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(providers): address LongCat review feedback
- Add dedicated factory routing test for LongCat provider
- Add longcat to DefaultAPIBase test coverage
- Set default api_base in example config providers section
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(providers): add ResolveProviderSelection tests for LongCat
Add two test cases to TestResolveProviderSelection:
- Explicit provider selection with api_base default and proxy wiring
- Fallback inference from model name with api_base default
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Non-OpenAI providers (Mistral, DeepSeek, Groq, etc.) reject unknown
request fields with 422 errors. The previous blocklist only excluded
Google/Gemini, but the comment already noted this feature is
OpenAI-only. Flip to an allowlist so only api.openai.com receives
the field.
Fixes#1333
* feat(auth): add Anthropic OAuth setup-token login flow
Add support for Anthropic's OAuth-based setup tokens (sk-ant-oat01-*)
as an alternative to API keys. This includes:
- New `--setup-token` flag on `auth login` command
- Interactive login menu for Anthropic (setup token vs API key)
- Setup token validation and credential storage with oauth auth method
- Usage endpoint integration to show 5h/7d utilization in `auth status`
- Streaming support for OAuth tokens (required by Anthropic API)
- Model ID normalization (dots to hyphens) for API compatibility
- Remove .env.example (secrets should not be templated)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(auth): update related functionality
* refactor(auth): organize constants and improve header casing in requests fo CI
* fix(auth): fix golint again
* fix(auth): handle nil arguments in tool calls for buildParams function
---------
Co-authored-by: Baller <sharonms3377@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
io.ReadAll errors were silently discarded with `body, _ := io.ReadAll(...)`,
which could cause empty or partial data to be used for JSON unmarshaling
or error messages. This adds proper error checks for all instances.
* feat: add extended thinking support for Anthropic models
Support configurable thinking levels (off/low/medium/high/xhigh/adaptive)
via `agents.defaults.thinking_level` config field.
- "adaptive": uses Anthropic's adaptive thinking API (Claude 4.6+)
- "low/medium/high/xhigh": uses budget_tokens (all thinking-capable models)
- "off": disables thinking (default)
API constraints handled:
- Temperature cleared when thinking is enabled
- budget_tokens clamped to max_tokens-1
- Thinking response blocks parsed into Reasoning field
Relates to #645, #966
* fix: address PR review feedback for thinking support
- Add ThinkingCapable interface for provider capability detection
- Warn when thinking_level is set but provider doesn't support it
- Warn when temperature is cleared due to thinking enabled
- Adjust budget values per Anthropic best practices (medium=16K, xhigh=64K)
- Add budget clamp warning and 80% threshold warning
- Add parseResponse thinking block tests
- Add thinking_level field to config.example.json
* refactor: move ThinkingLevel from AgentDefaults to ModelConfig
Thinking is a model-level capability, not a global agent property.
Per-model config avoids silent ignoring on non-Anthropic providers
and eliminates spurious warning logs in multi-provider setups.
Addresses PR #1076 review feedback from @yinwm.
Add Avian (https://avian.io) as an OpenAI-compatible provider with
API base https://api.avian.io/v1 and AVIAN_API_KEY env var support.
Models: deepseek/deepseek-v3.2, moonshotai/kimi-k2.5, z-ai/glm-5,
minimax/minimax-m2.5. Supports chat completions, streaming, and
function calling.
Changes:
- Add Avian to ProvidersConfig struct, IsEmpty(), HasProvidersConfig()
- Add avian protocol to factory provider and default API base
- Add avian case to legacy provider selection (factory.go)
- Add avian migration rule for old config format
- Add default model entries to ModelList (deepseek-v3.2, kimi-k2.5)
- Add avian to example config
- Update AllProviders test count from 18 to 19
- Separate third-party imports from local module imports (gci)
- Fix byte slice literal formatting (gofumpt)
- Rename shadowed err variable to ftErr (govet)
- Remove trailing blank lines in test files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- serializeMessages: preserve ToolCallID/ToolCalls when Media is present
- resolveMediaRefs: add 20MB file size limit to prevent OOM
- mimeFromExtension: return empty string for unknown extensions
- Add 11 unit tests for serializeMessages, resolveMediaRefs, mimeFromExtension
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The serializeMessages() function was not preserving the reasoning_content
field when serializing messages for vision API calls. This caused the
TestProviderChat_PreservesReasoningContentInHistory test to fail.
This fix ensures reasoning_content is included in both text-only messages
and vision messages with media attachments.
Co-authored-by: Zachary Guerrero <zack.grrr@gmail.com>
- Add Media []string field to Message struct for image/media URLs
- Implement serializeMessages() to format messages with image_url content parts
- Enables OpenAI-compatible vision APIs to receive image attachments
* fix(pkg/providers):do regex precompile insteadd on the fly
* fix(providers): replace HTTP-specific regex with standalone status code matcher
The precompiled HTTP regex used uppercase "HTTP" which never matched
because ClassifyError lowercases the input. Replace it with a
case-insensitive word-boundary pattern that matches any standalone
3-digit status code (300-599), which also subsumes the HTTP/x.x case.
Add test case for standalone status code extraction.
* fix(providers): restore http regex and add standalone status code matcher
Restore the http-prefixed regex (without unnecessary (?i) flag since
input is already lowercased by ClassifyError) as a mid-priority pattern
to reduce false positives. Add a standalone word-boundary matcher as a
fallback for bare status codes like "429". Fix test to use lowercased
input matching the actual calling convention.
* perf(tools): move path regex compilation from per-call to package init
The path regex in guardCommand was compiled on every call. Hoist it
to a package-level var (absolutePathPattern) alongside defaultDenyPatterns
in a single var block, so it is compiled once at init time.
* style(tools): move inline comment to fix golines formatting error
- Fix ignored error from SendAndWait call
- Improve error message for unimplemented stdio mode with helpful guidance
- Add TODO comment with reference link for future stdio implementation
The openaiMessage struct and stripSystemParts() were not carrying over
the ReasoningContent field when serializing conversation history for
API requests. This caused thinking models (e.g. kimi-k2.5) to receive
incomplete assistant messages on subsequent turns, resulting in 400
errors from the Moonshot API.
Add the ReasoningContent field to openaiMessage and copy it in
stripSystemParts(). Also add a test to verify reasoning_content is
preserved when sending conversation history.
Fixes#588
Related: #876
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Whitespace is a linter that checks for unnecessary newlines at the start and end of functions, if, for, etc.
Signed-off-by: Kai Xia <kaix+github@fastmail.com>
Avoid rebuilding the entire system prompt on every BuildMessages() call
by caching the static portion (identity, bootstrap, skills summary,
memory) and only recomputing it when workspace source files change.
Key changes:
- ContextBuilder caches the static prompt behind an RWMutex with
double-checked locking. Source file changes are detected via cheap
os.Stat mtime checks so no explicit invalidation is needed.
- Track file existence at cache time (existedAtCache map) so that
newly created or deleted bootstrap/memory files also trigger a
rebuild — the old modifiedSince() silently returned false on
os.IsNotExist.
- Walk the skills directory recursively with filepath.WalkDir to
catch content-only edits at any nesting depth; directory mtime
alone misses in-place file modifications on most filesystems.
- ToolRegistry.sortedToolNames() sorts tool names before iteration,
ensuring deterministic tool definition order across calls — a
prerequisite for LLM-side prefix/KV cache reuse.
- Merge all context (static + dynamic + summary) into a single
system message for provider compatibility: the Anthropic adapter
extracts messages[0] as the top-level system parameter, and Codex
reads only the first system message as instructions.
- Fix a data race in BuildMessages() where cachedSystemPrompt was
read without holding the lock in a debug log statement.
- Add tests: single system message invariant, mtime auto-invalidation,
new-file creation detection, skill file content change, explicit
InvalidateCache, cache stability, concurrent access (20 goroutines
x 50 iterations, passes go test -race), and a benchmark.
* cleanup dead code.
Signed-off-by: Kai Xia <kaix+github@fastmail.com>
* add these two back with flag.
Signed-off-by: Kai Xia <kaix+github@fastmail.com>
* fix ci
Signed-off-by: Kai Xia <kaix+github@fastmail.com>
* remove this confusing line
Signed-off-by: Kai Xia <kaix+github@fastmail.com>
* make fmt
Signed-off-by: Kai Xia <kaix+github@fastmail.com>
* remove unused method.
picked up by golangci-lint run
Signed-off-by: Kai Xia <kaix+github@fastmail.com>
---------
Signed-off-by: Kai Xia <kaix+github@fastmail.com>
The configuration field for specifying the model has been renamed from
"model" to "model_name" for better clarity and consistency with the
model_list configuration.
A GetModelName() accessor method has been added to maintain backward
compatibility. Existing configurations using the old "model" field will
continue to work correctly.
This change affects:
- Configuration structure (AgentDefaults struct)
- All references across the codebase
- Documentation in all language variants
- Example configuration files
Mistral's API strictly validates tool_calls in assistant messages and
rejects non-standard fields. The ToolCall struct had Name and Arguments
as top-level JSON fields, duplicating data already in Function.Name
and Function.Arguments. OpenAI silently ignored these extras but
Mistral returns 422.
Change json tags to "-" so these internal fields are no longer
serialized to API payloads while remaining available in Go code.
Add Mistral as a first-class provider alongside the 17 existing ones.
Mistral uses the OpenAI-compatible API at https://api.mistral.ai/v1
with provider-specific model prefix stripping (mistral/model → model).
Changes:
- Add Mistral to ProvidersConfig, IsEmpty(), HasProvidersConfig()
- Add mistral entry in default model_list (defaults.go)
- Add mistral protocol in factory_provider.go and getDefaultAPIBase()
- Add mistral prefix stripping in openai_compat normalizeModel()
- Add mistral case in legacy factory.go resolveProviderSelection()
- Add mistral migration entry in ConvertProvidersToModelList()
- Add mistral to supported providers in migrate/config.go
- Add mistral section in config.example.json
- Update AllProviders test (17 → 18 providers)
Tested end-to-end with mistral-small-latest model.
Models like Moonshot kimi-k2.5 and DeepSeek-R1 return a
reasoning_content field in assistant messages. When thinking is enabled,
the API requires this field to be echoed back in subsequent requests.
PicoClaw was silently dropping it, causing 400 errors on tool-call
round-trips.
- Add ReasoningContent to Message and LLMResponse types
- Parse reasoning_content in openai_compat parseResponse()
- Carry reasoning_content through assistant tool-call messages
- Add unit test for reasoning_content parsing
Fixes#588
Replace all claude-sonnet-4 references with claude-sonnet-4.6 across
codebase including documentation, tests, and configuration examples.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove sync.RWMutex and rrCounters from Config struct
- Simplify GetModelConfig to use global atomic counter for load balancing
- Remove unnecessary locks from HasProvidersConfig, SaveConfig, etc.
- Add buildModelWithProtocol helper to handle models with existing prefix
- Fix TestCreateProviderReturnsHTTPProviderForOpenRouter to use model_list
- Upgrade all Claude 3 references to Claude 4 across documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auth fixes:
- Fix OpenAI/Anthropic OAuth and token login to update ModelList
- Fix logout to clear AuthMethod in ModelList
- Add helper functions: isOpenAIModel, isAnthropicModel, isAntigravityModel
- Fix slice bounds panic in isAntigravityModel using strings.HasPrefix
- All auth operations now preserve existing model_list configuration
Factory provider fixes:
- Add OAuth support for openai protocol in CreateProviderFromConfig
- CodexAuthProvider is now used when auth_method is oauth/token
Default model updates:
- OpenAI login: set default model to gpt-5.2
- Anthropic login: set default model to claude-sonnet-4
- Antigravity login: set default model to gemini-flash (remove provider field)
Model changes:
- Change default OpenAI model from gpt-4o to gpt-5.2
- gpt-5.2 is compatible with Codex API (chatgpt.com backend)
- Update all README files, config examples, and migration code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add support for persisting thought_signature metadata from Google/Gemini 3
models. This introduces ExtraContent and GoogleExtra types to handle
provider-specific metadata, and ensures thought signatures are properly
preserved through the tool call lifecycle.
- Move provider creation logic to factory_provider.go with protocol-based approach
- Add OpenAIProviderConfig with WebSearch support and embedded ProviderConfig
- Add maxTokensField to OpenAI-compatible provider for configurable token field
- Introduce new providers: Ollama, DeepSeek, GitHubCopilot, Antigravity, Qwen
- Remove redundant CreateProvider function from factory.go
- Add ThoughtSignature field to FunctionCall for tool response handling
- Remove duplicate Name field assignment in tool loop
- Update tests to reflect new provider configuration structure
- Preserve user's configured model during config migration (issue #5)
- Simplify ExtractProtocol using strings.Cut
- Extract NormalizeToolCall to shared utility, removing ~70 lines of duplicate code
- Clean up unused fields in providerMigrationConfig struct
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>