mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 23:10:11 +00:00
* feat: add Codex auth.json token reuse for LLM authentication When LLM_USE_CODEX_AUTH=true, IronClaw reads the Codex CLI's auth.json (default ~/.codex/auth.json) and extracts the API key or OAuth access token. This lets IronClaw piggyback on a Codex login without implementing its own OAuth flow. New env vars: - LLM_USE_CODEX_AUTH: enable Codex auth fallback (default: false) - CODEX_AUTH_PATH: override path to auth.json * fix: handle ChatGPT auth mode correctly Switch base_url to chatgpt.com/backend-api/codex when auth.json contains ChatGPT OAuth tokens. The access_token is a JWT that only works against the private ChatGPT backend, not the public OpenAI API. Refactored codex_auth.rs to return CodexCredentials (token + is_chatgpt_mode) instead of just a string key. * fix: Codex auth takes highest priority over secrets store When LLM_USE_CODEX_AUTH=true, Codex credentials are now loaded before checking env vars or the secrets store overlay. Previously the secrets store key (injected during onboarding) would shadow the Codex token. * feat: Responses API provider for ChatGPT backend - New CodexChatGptProvider speaks the Responses API protocol - Auto-detects model from /models endpoint (gpt-4o -> gpt-5.2-codex) - Adds store=false (required by ChatGPT backend) - Error handling with timeout for HTTP 400 responses - Message format translation: Chat Completions -> Responses API - SSE response parsing for text, tool calls, and usage stats - 7 unit tests for message conversion and SSE parsing * fix: SSE parser uses item_id instead of call_id for tool call deltas The Responses API sends function_call_arguments.delta events with item_id (e.g. fc_...) not call_id (e.g. call_...). The parser now keys pending tool calls by item_id from output_item.added and tracks call_id separately for result matching. * fix: strip empty string values from tool call arguments gpt-5.2-codex fills optional tool parameters with empty strings (e.g. timestamp: ""), which IronClaw's tool validation rejects. Strip them before passing to tool execution. * fix: prevent apiKey mode fallback to ChatGPT token When auth_mode is explicitly 'apiKey' but the key is missing/empty, do not fall through to check for a ChatGPT access_token. This prevents returning credentials with is_chatgpt_mode: true and routing to the wrong LLM provider. * refactor: reuse single reqwest::Client across model discovery and LLM calls Create Client once in with_auto_model, pass &Client to fetch_default_model, and move it into the provider struct. Eliminates the redundant Client::new() that wasted a connection pool. * fix: bump client_version to 1.0.0 to unlock gpt-5.3-codex and gpt-5.4 The /models endpoint gates newer models behind client_version. Version 0.1.0 only returns up to gpt-5.2-codex, while 1.0.0+ also returns gpt-5.3-codex and gpt-5.4. * feat: user-configured LLM_MODEL takes priority over auto-detection Fetch the full model list from /models endpoint. If LLM_MODEL is set, validate it against the supported list and warn with available models if not found. If LLM_MODEL is not set, auto-detect the highest-priority model. Also bumps client_version to 1.0.0 to unlock gpt-5.3/5.4. * fix: add 10s timeout to model discovery HTTP request Prevents startup from blocking indefinitely if chatgpt.com is slow or unreachable. Uses reqwest per-request timeout. * docs: add private API warning for ChatGPT backend endpoint The chatgpt.com/backend-api/codex endpoint is private and undocumented. Add warning in module docs and a runtime log on first use to inform users of potential ToS implications. * feat: implement OAuth 401 token refresh for Codex ChatGPT provider On HTTP 401, if a refresh_token is available, the provider now automatically refreshes the access token via auth.openai.com/oauth/token (same protocol as Codex CLI) and retries the request once. Refreshed tokens are persisted back to auth.json. Changes: - codex_auth: read refresh_token, add refresh_access_token() and persist_refreshed_tokens() - codex_chatgpt: RwLock for api_key, 401 detection + retry in send_request, send_http_request helper - config/llm: thread refresh_token/auth_path through RegistryProviderConfig - llm/mod: pass refresh params to with_auto_model * refactor: lazy model detection via OnceCell, remove block_in_place Model is no longer resolved during provider construction. Instead, resolve_model() uses tokio::sync::OnceCell to lazily fetch from /models on the first LLM call. This eliminates the block_in_place + block_on workaround in create_codex_chatgpt_from_registry. - with_auto_model (async) -> with_lazy_model (sync constructor) - resolve_model() added with OnceCell-based lazy init - build_request_body takes model as parameter - model_name() returns resolved or configured_model as fallback * feat: support multimodal content (images) in Codex ChatGPT provider message_to_input_items now checks content_parts for user messages. ContentPart::Text maps to input_text and ContentPart::ImageUrl maps to input_image, matching the Responses API format used by Codex CLI. Falls back to plain text when content_parts is empty. Also updates client_version to 0.111.0 for /models endpoint. Adds test: test_message_conversion_user_with_image * refactor: move codex_auth module from src/ to src/llm/ codex_auth is only used by the LLM layer (codex_chatgpt provider and config/llm). Moving it under src/llm/ reflects its actual scope. - Remove pub mod codex_auth from lib.rs - Add pub mod codex_auth to llm/mod.rs - Update imports: super::codex_auth, crate::llm::codex_auth * Fix codex provider style issues * Use SecretString throughout codex auth refresh flow * Use SecretString for codex access tokens * Reuse provider client for codex token refresh * Stream Codex SSE responses incrementally * Fix Windows clippy and SQLite test linkage * Trigger checks after regression skip label * Tighten codex auth module handling
190 lines
7.6 KiB
Bash
190 lines
7.6 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, tinfoil
|
||
# 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
|
||
|
||
# === 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=zai-org/GLM-5-FP8
|
||
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.5
|
||
# 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
|
||
|
||
# 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
|