mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
* feat: integrate Gemini CLI OAuth with Cloud Code API
- Add gemini_oauth.rs: full OAuth flow with PKCE, token refresh,
and Cloud Code project discovery (loadCodeAssist + onboardUser)
- Route preview/gemini-3 models through cloudcode-pa.googleapis.com
with proper project ID injection in request payload
- Trigger OAuth login during onboarding wizard (not first chat message)
- Support manual redirect URL paste as fallback (tokio::select race)
- Parse 429 rate-limit errors with retry_after from Google response
- Add static model list: gemini-1.5/2.0/2.5/3.0/3.1 variants
- Add GeminiOauthConfig with default credentials path (~/.gemini/)
* feat(gemini): implement function calling, generationConfig, and update models
- Implement function calling support (functionDeclarations, functionResponse)
- Add functionCall SSE parsing and empty stream retry support
- Add generationConfig (temperature, maxOutputTokens)
- Add thinkingConfig for Gemini 3 and thinking models
- Add toolConfig (functionCallingConfig.mode)
- Fix .expect() panics with .ok_or_else()
- Restrict oauth credentials file permissions to 0600
- Update docs and FEATURE_PARITY.md
- Update wizard to current Gemini 3.1 and 2.5 models
* fix: address code review issues in gemini-cli OAuth integration
- Add cache_read_input_tokens/cache_creation_input_tokens fields (value 0)
- Implement manual Debug for OAuthCredential to redact tokens
- Fix hardcoded /tmp: use GeminiOauthConfig::default_credentials_path()
- Replace emoji output with plain text markers
- Propagate Client::builder() errors instead of silent fallback
- Use tokio::fs for all file I/O in CredentialManager (was std::fs)
- Use if let Some(ref pid) to avoid consuming credential.project_id
- Extract uses_cloud_code_api() helper; route by major version (gemini-2+)
- Concatenate multiple system messages into systemInstruction
- Include functionCall parts in assistant message conversion
- Add 401 retry loop with allow_retry flag for auth failures
- Remove biased from tokio::select! in OAuth callback handler
- Remove hardcoded context_length 1M; vary by model family
- Change GOOG_API_CLIENT from Node.js spoof to gl-rust/1.0.0
- Implement list_models() with static model list
- Move create_gemini_oauth_provider() before test module (clippy)
- Fix 9 additional clippy warnings (collapsible_if, map_or, needless_borrow)
- Run cargo fmt
* Add dedicated regression tests for Gemini OAuth fixes
* style: fix formatting in Gemini OAuth regression tests
* feat(gemini-oauth): implement code review v3 refinements
- Add force_refresh() for 401 retry (bypass timestamp check)
- Standardize Gemini model list across docs, wizard, and provider
- Restore gemini-3 check for thinkingConfig
- Redact sensitive tokens in GoogleTokenRefreshResponse Debug output
- Use dynamic version for GOOG_API_CLIENT
- Improve model_metadata() context length heuristics
- Use strip_prefix("data:") for safer SSE parsing
- Skip re-auth in wizard if keeping existing provider
* feat(gemini_oauth): full Cloud Code API integration with project discovery
- Register gemini_oauth as a dedicated backend in config/llm.rs (skip
registry fallback, preserve backend name, suppress unknown-backend warning)
- Fix app.rs credential guard to exclude backends with dedicated configs
(gemini_oauth, bedrock) from the provider.is_none() check
- Auto-discover Cloud Code project_id via loadCodeAssist when credentials
lack it (e.g. created by the original Gemini CLI)
- Persist discovered project_id to credentials file for subsequent runs
- Add safety settings (BLOCK_NONE), gated behind GEMINI_SAFETY_BLOCK_NONE env
- Add thinkingConfig: budget-based for Gemini 2.5, level-based for Gemini 3.x
(without includeThoughts to avoid empty responses from reasoning.rs stripping)
- Add thought signature injection for Gemini 3.x preview APIs
- Add history curation to filter invalid model outputs before re-sending
- Add extended generationConfig env vars (topP, topK, seed, penalties,
responseMimeType, responseJsonSchema, cachedContent)
- Add custom headers support via GEMINI_CLI_CUSTOM_HEADERS
- Add API key auth mode (GEMINI_API_KEY + GEMINI_API_KEY_AUTH_MECHANISM)
- Add SSE metadata extraction (modelVersion, credits, promptFeedback,
groundingMetadata, citationMetadata, cachedContentTokenCount)
- Add countTokens API support
- Add new models to wizard (gemini-3.1-pro-preview-customtools,
gemini-3-pro-preview, gemini-3.1-flash-lite-preview)
- Update docs/LLM_PROVIDERS.md with new models and routing rules
- Rewrite regression tests with comprehensive coverage (23 unit tests pass)
* fix: CI violations — add safety comment on expect, fix fmt
- Add '// safety: hardcoded literal' to regex .expect() to satisfy
the no-panic-in-prod CI check
- Fix cargo fmt whitespace in collapsible if-let chain
* fix: address PR review feedback from gemini-code-assist
- Fix parse_custom_headers to preserve commas in values by splitting
only on commas followed by a header-name:colon pattern (manual scan
instead of simple split(','))
- Use matches! macro for backend exclusion check in app.rs
- Merge SSE metadata extraction into single pass (was iterating twice)
- Replace fragile substring-based context_length with explicit match
on known Gemini model IDs via gemini_context_length()
- Add missing models to regression test (8 models, not 5)
* fix: address Copilot PR review feedback
- Fix empty text part for assistant messages with tool calls
(curate_contents could drop entire model turn)
- Propagate cache_read/creation_input_tokens in complete_with_tools
- Log warning on save_credential failure instead of silently ignoring
- Fix doc comment to mention underscore in header name pattern
- Handle gemini-oauth (hyphen variant) in setup wizard display
- Fix docs: thinkingConfig uses thinkingBudget/thinkingLevel, not
includeThoughts
* fix: add missing allow_always field after staging merge
* fix(gemini_oauth): align header parser doc with implementation [skip-regression-check]
Update parse_custom_headers doc comments to include underscore in the
header-name character class, matching the actual implementation.
Also fix formatting from merge.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(gemini_oauth): curate_contents per-part filtering and dead code removal
Fix curate_contents to filter invalid parts individually instead of
dropping entire model turn sequences. Previously a single empty text
part would discard all consecutive model turns including valid
functionCall parts, breaking the tool-call flow.
Also remove unused MID_STREAM_* constants.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* style(gemini_oauth): rustfmt formatting [skip-regression-check]
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(llm): support smart routing cheap model for gemini_oauth backend
Add explicit gemini_oauth handling in create_cheap_provider_for_backend()
to create a GeminiOauthProvider with the cheap model swapped in. Without
this, setting LLM_CHEAP_MODEL with gemini_oauth backend would fail with
a confusing "no registry provider config available" error.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* docs: add Gemini OAuth env vars to .env.example [skip-regression-check]
Document GEMINI_MODEL, GEMINI_CREDENTIALS_PATH, GEMINI_API_KEY, and
all extended generation config env vars in the example config file.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---------
Co-authored-by: [email protected] <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
225 lines
9.3 KiB
Bash
225 lines
9.3 KiB
Bash
# Database Configuration
|
||
DATABASE_URL=postgres://localhost/ironclaw
|
||
DATABASE_POOL_SIZE=10
|
||
|
||
# LLM Provider
|
||
# LLM_BACKEND=nearai # default
|
||
# Possible values: nearai, ollama, openai_compatible, openai, anthropic, github_copilot, tinfoil, openai_codex, gemini_oauth
|
||
# LLM_REQUEST_TIMEOUT_SECS=120 # Increase for local LLMs (Ollama, vLLM, LM Studio)
|
||
|
||
# === Anthropic Direct ===
|
||
# Two auth modes:
|
||
# 1. API key: Set ANTHROPIC_API_KEY (from console.anthropic.com/settings/keys)
|
||
# 2. OAuth token: Set ANTHROPIC_OAUTH_TOKEN (from `claude login`)
|
||
# OAuth tokens use Authorization: Bearer instead of x-api-key header.
|
||
# ANTHROPIC_API_KEY=sk-ant-...
|
||
# ANTHROPIC_OAUTH_TOKEN=sk-ant-oat01-... # from `claude login` credentials
|
||
# ANTHROPIC_MODEL=claude-sonnet-4-20250514
|
||
|
||
# === OpenAI Direct ===
|
||
# OPENAI_API_KEY=sk-...
|
||
# Reuse Codex CLI auth.json instead of setting OPENAI_API_KEY manually.
|
||
# Works with both OpenAI API-key mode and Codex ChatGPT OAuth mode.
|
||
# In ChatGPT mode this uses the private `chatgpt.com/backend-api/codex` endpoint.
|
||
# LLM_USE_CODEX_AUTH=true
|
||
# CODEX_AUTH_PATH=~/.codex/auth.json
|
||
|
||
# === GitHub Copilot ===
|
||
# Uses the OAuth token from your Copilot IDE sign-in (for example
|
||
# ~/.config/github-copilot/apps.json on Linux/macOS), or run `ironclaw onboard`
|
||
# and choose the GitHub device login flow.
|
||
# LLM_BACKEND=github_copilot
|
||
# GITHUB_COPILOT_TOKEN=gho_...
|
||
# GITHUB_COPILOT_MODEL=gpt-4o
|
||
# IronClaw injects standard VS Code Copilot headers automatically.
|
||
# Optional advanced headers for custom overrides:
|
||
# GITHUB_COPILOT_EXTRA_HEADERS=Copilot-Integration-Id:vscode-chat
|
||
|
||
# === NEAR AI (Chat Completions API) ===
|
||
# Two auth modes:
|
||
# 1. Session token (default): Uses browser OAuth (GitHub/Google) on first run.
|
||
# Session token stored in ~/.ironclaw/session.json automatically.
|
||
# Base URL defaults to https://private.near.ai
|
||
# 2. API key: Set NEARAI_API_KEY to use API key auth from cloud.near.ai.
|
||
# Base URL defaults to https://cloud-api.near.ai
|
||
NEARAI_MODEL=Qwen/Qwen3.5-122B-A10B
|
||
NEARAI_BASE_URL=https://private.near.ai
|
||
NEARAI_AUTH_URL=https://private.near.ai
|
||
# NEARAI_SESSION_TOKEN=sess_... # hosting providers: set this
|
||
# NEARAI_SESSION_PATH=~/.ironclaw/session.json # optional, default shown
|
||
# NEARAI_API_KEY=... # API key from cloud.near.ai
|
||
|
||
# Local LLM Providers (Ollama, LM Studio, vLLM, LiteLLM)
|
||
|
||
# === Ollama ===
|
||
# OLLAMA_MODEL=llama3.2
|
||
# LLM_BACKEND=ollama
|
||
# OLLAMA_BASE_URL=http://localhost:11434 # default
|
||
|
||
# === OpenAI-compatible (LM Studio, vLLM, Anything-LLM) ===
|
||
# LLM_MODEL=llama-3.2-3b-instruct-q4_K_M
|
||
# LLM_BACKEND=openai_compatible
|
||
# LLM_BASE_URL=http://localhost:1234/v1
|
||
# LLM_API_KEY=sk-... # optional for local servers
|
||
# Custom HTTP headers for OpenAI-compatible providers
|
||
# Format: comma-separated key:value pairs
|
||
# LLM_EXTRA_HEADERS=HTTP-Referer:https://github.com/nearai/ironclaw,X-Title:ironclaw
|
||
|
||
# === OpenRouter (300+ models via OpenAI-compatible) ===
|
||
# LLM_MODEL=anthropic/claude-sonnet-4 # see openrouter.ai/models for IDs
|
||
# LLM_BACKEND=openai_compatible
|
||
# LLM_BASE_URL=https://openrouter.ai/api/v1
|
||
# LLM_API_KEY=sk-or-...
|
||
|
||
# LLM_EXTRA_HEADERS=HTTP-Referer:https://myapp.com,X-Title:MyApp
|
||
|
||
|
||
# === Together AI (via OpenAI-compatible) ===
|
||
# LLM_MODEL=meta-llama/Llama-3.3-70B-Instruct-Turbo
|
||
# LLM_BACKEND=openai_compatible
|
||
# LLM_BASE_URL=https://api.together.xyz/v1
|
||
# LLM_API_KEY=...
|
||
|
||
# === Fireworks AI (via OpenAI-compatible) ===
|
||
# LLM_MODEL=accounts/fireworks/models/llama4-maverick-instruct-basic
|
||
# LLM_BACKEND=openai_compatible
|
||
# LLM_BASE_URL=https://api.fireworks.ai/inference/v1
|
||
# LLM_API_KEY=fw_...
|
||
|
||
# === MiniMax ===
|
||
# LLM_BACKEND=minimax
|
||
# MINIMAX_API_KEY=...
|
||
# MINIMAX_MODEL=MiniMax-M2.7
|
||
# MINIMAX_BASE_URL=https://api.minimax.io/v1 # default (global); use https://api.minimaxi.com/v1 for China
|
||
|
||
# === Anthropic Direct ===
|
||
# LLM_BACKEND=anthropic
|
||
# ANTHROPIC_MODEL=claude-sonnet-4-6
|
||
# ANTHROPIC_API_KEY=sk-ant-...
|
||
# ANTHROPIC_BASE_URL=https://api.anthropic.com # default
|
||
# Prompt cache retention — controls Anthropic server-side prompt caching:
|
||
# none = disabled (no cache_control injected)
|
||
# short = 5-minute TTL, 1.25× (125%) write surcharge (default)
|
||
# long = 1-hour TTL, 2.0× (200%) write surcharge
|
||
# ANTHROPIC_CACHE_RETENTION=short
|
||
|
||
# === OpenAI Codex (ChatGPT subscription, OAuth) ===
|
||
# LLM_BACKEND=openai_codex
|
||
# OPENAI_CODEX_MODEL=gpt-5.3-codex # default
|
||
# OPENAI_CODEX_CLIENT_ID=app_EMoamEEZ73f0CkXaXp7hrann # override (rare)
|
||
# OPENAI_CODEX_AUTH_URL=https://auth.openai.com # override (rare)
|
||
# OPENAI_CODEX_API_URL=https://chatgpt.com/backend-api/codex # override (rare)
|
||
|
||
# === Google Gemini (OAuth, Gemini CLI compatible) ===
|
||
# LLM_BACKEND=gemini_oauth
|
||
# GEMINI_MODEL=gemini-2.5-flash # default
|
||
# GEMINI_CREDENTIALS_PATH=~/.gemini/oauth_creds.json # default
|
||
# GEMINI_API_KEY=... # optional: use API key instead of OAuth
|
||
# GEMINI_API_KEY_AUTH_MECHANISM=query # "query" (default) or "header"
|
||
# GEMINI_SAFETY_BLOCK_NONE=true # disable safety filters (default: false)
|
||
# GEMINI_CLI_CUSTOM_HEADERS=Key:Value,Key2:Value2
|
||
# GEMINI_TOP_P=0.95
|
||
# GEMINI_TOP_K=40
|
||
# GEMINI_SEED=42
|
||
# GEMINI_PRESENCE_PENALTY=0.0
|
||
# GEMINI_FREQUENCY_PENALTY=0.0
|
||
# GEMINI_RESPONSE_MIME_TYPE=application/json
|
||
# GEMINI_RESPONSE_JSON_SCHEMA={"type":"object"}
|
||
# GEMINI_CACHED_CONTENT=cachedContents/abc123
|
||
|
||
# For full provider setup guide see docs/LLM_PROVIDERS.md
|
||
|
||
# Channel Configuration
|
||
# CLI is always enabled
|
||
|
||
# Slack Bot (optional)
|
||
SLACK_BOT_TOKEN=xoxb-...
|
||
SLACK_APP_TOKEN=xapp-...
|
||
SLACK_SIGNING_SECRET=...
|
||
|
||
# Telegram Bot (optional)
|
||
TELEGRAM_BOT_TOKEN=...
|
||
|
||
# HTTP Webhook Server (optional)
|
||
HTTP_HOST=0.0.0.0
|
||
HTTP_PORT=8080
|
||
HTTP_WEBHOOK_SECRET=your-webhook-secret
|
||
# Webhook authentication uses HMAC-SHA256 signature verification.
|
||
# Callers must send an X-IronClaw-Signature header with format: sha256=<hex_digest>
|
||
# where the digest is HMAC-SHA256(HTTP_WEBHOOK_SECRET, raw_request_body) in lowercase hex.
|
||
#
|
||
# Example (bash):
|
||
# BODY='{"content":"hello"}'
|
||
# SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$HTTP_WEBHOOK_SECRET" | cut -d' ' -f2)
|
||
# curl -X POST http://localhost:8080/webhook \
|
||
# -H "Content-Type: application/json" \
|
||
# -H "X-IronClaw-Signature: sha256=$SIG" \
|
||
# -d "$BODY"
|
||
#
|
||
# DEPRECATED: Passing "secret" in the JSON body still works but will be removed in a future release.
|
||
|
||
# Signal Channel (optional, requires signal-cli daemon --http)
|
||
# SIGNAL_HTTP_URL=http://127.0.0.1:8080
|
||
# SIGNAL_ACCOUNT=+1234567890
|
||
# SIGNAL_ALLOW_FROM=+1234567890,uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # comma-separated, * for all, empty = deny/require pairing
|
||
# SIGNAL_ALLOW_FROM_GROUPS= # comma-separated group IDs, * for all, empty = deny all groups
|
||
# SIGNAL_DM_POLICY=pairing # open | allowlist | pairing
|
||
# SIGNAL_GROUP_POLICY=allowlist # allowlist | open | disabled
|
||
# SIGNAL_GROUP_ALLOW_FROM= # comma-separated, empty = inherit from ALLOW_FROM
|
||
# SIGNAL_IGNORE_ATTACHMENTS=false
|
||
# SIGNAL_IGNORE_STORIES=true
|
||
|
||
# Agent Settings
|
||
AGENT_NAME=ironclaw
|
||
AGENT_MAX_PARALLEL_JOBS=5
|
||
AGENT_JOB_TIMEOUT_SECS=3600
|
||
AGENT_STUCK_THRESHOLD_SECS=300
|
||
# Maximum tokens per job (0 = unlimited, also settable via settings.json agent.max_tokens_per_job)
|
||
# AGENT_MAX_TOKENS_PER_JOB=0
|
||
# Enable planning phase before tool execution (default: true)
|
||
AGENT_USE_PLANNING=true
|
||
|
||
# Self-repair settings
|
||
SELF_REPAIR_CHECK_INTERVAL_SECS=60
|
||
SELF_REPAIR_MAX_ATTEMPTS=3
|
||
|
||
# Heartbeat settings (proactive periodic execution)
|
||
# When enabled, reads HEARTBEAT.md checklist and reports findings
|
||
HEARTBEAT_ENABLED=false
|
||
HEARTBEAT_INTERVAL_SECS=1800
|
||
HEARTBEAT_NOTIFY_CHANNEL=cli
|
||
HEARTBEAT_NOTIFY_USER=default
|
||
|
||
# Memory hygiene settings (automatic cleanup of stale workspace documents)
|
||
# Runs on each heartbeat tick; identity files (IDENTITY.md, SOUL.md) are never deleted
|
||
# MEMORY_HYGIENE_ENABLED=true
|
||
# MEMORY_HYGIENE_DAILY_RETENTION_DAYS=30 # delete daily/ docs older than this many days
|
||
# MEMORY_HYGIENE_CONVERSATION_RETENTION_DAYS=7 # delete conversations/ docs older than this many days
|
||
# MEMORY_HYGIENE_CADENCE_HOURS=12 # minimum hours between cleanup passes
|
||
|
||
# Docker Sandbox
|
||
# SANDBOX_ENABLED=true
|
||
# SANDBOX_POLICY=readonly # readonly, workspace_write, or full_access
|
||
# SANDBOX_ALLOW_FULL_ACCESS=false # REQUIRED second opt-in for full_access policy.
|
||
# # FullAccess bypasses Docker entirely and runs
|
||
# # commands directly on the host. Without this
|
||
# # set to "true", full_access is downgraded to
|
||
# # workspace_write.
|
||
# SANDBOX_IMAGE=ironclaw-worker:latest
|
||
# SANDBOX_TIMEOUT_SECS=120
|
||
# SANDBOX_MEMORY_LIMIT_MB=2048
|
||
|
||
# Safety settings
|
||
SAFETY_MAX_OUTPUT_LENGTH=100000
|
||
SAFETY_INJECTION_CHECK_ENABLED=true
|
||
|
||
# Restart Feature (Docker containers only)
|
||
# Set IRONCLAW_IN_DOCKER=true in the container entrypoint to enable the restart feature.
|
||
# Without this, the restart tool and /restart command will be disabled.
|
||
# IRONCLAW_IN_DOCKER=false
|
||
# IRONCLAW_RESTART_DELAY=5 # default wait before exit (seconds, range: 1-30)
|
||
# IRONCLAW_MAX_FAILURES=10 # max consecutive failures before container exits
|
||
|
||
# Logging
|
||
RUST_LOG=ironclaw=debug,tower_http=debug
|