# Copy to .env and fill in. # --- LLM provider --- # LLM_PROVIDER: claude-cli | anthropic | openai | gemini. # claude-cli = run inference through Claude Code headless mode (`claude -p`), # the sanctioned way to use a Claude subscription programmatically. # (Direct-API calls with a Claude Code OAuth token are policy-blocked.) LLM_PROVIDER=claude-cli CLAUDE_CLI_MODEL=sonnet CLAUDE_CLI_TIMEOUT=300 # On a session-limit hit ("resets 7:10pm"), pause until reset + buffer, then retry: CLAUDE_CLI_LIMIT_BUFFER_SECONDS=60 CLAUDE_CLI_LIMIT_MAX_WAITS=6 CLAUDE_CLI_LIMIT_FALLBACK_WAIT_SECONDS=3600 # Direct Anthropic API (real API key) — use LLM_PROVIDER=anthropic: LLM_MODEL=claude-sonnet-5 ANTHROPIC_EFFORT=low # low | medium | high | max ANTHROPIC_USE_KEYCHAIN=1 # ANTHROPIC_OAUTH_TOKEN= # ANTHROPIC_API_KEY= # OpenAI-compatible endpoint — works with OpenAI, OpenRouter, Together, Groq, # vLLM, Ollama, LM Studio, etc. Just point these three at any /chat/completions API: OPENAI_BASE_URL=https://api.openai.com/v1 OPENAI_API_KEY= OPENAI_MODEL=gpt-4o-mini OPENAI_JSON_MODE=1 # set 0 if the endpoint rejects response_format=json_object # There is NO mock fallback. On HTTP 429 the loop WAITS this long and retries # (honors Retry-After if present), so it grinds through tight rate limits. LLM_RATELIMIT_WAIT_SECONDS=900 LLM_RATELIMIT_MAX_RETRIES=24 # Other providers: GEMINI_API_KEY= GEMINI_MODEL=gemini-2.5-flash # --- SEC EDGAR filings harvester (free, no key; public accounts auto-matched) --- SEC_ENABLED=1 SEC_MAX_FILINGS_PER_ACCOUNT=5 # Scoring thresholds (design Phase 5) PRIORITY_THRESHOLD=0.72 STANDARD_THRESHOLD=0.45 # --- Exa.ai news/web harvester --- # Without a key, the news connector falls back to data/fixtures.json. EXA_API_KEY= EXA_ENABLED=1 # Hard ceilings to stay inside the free plan (~20,000 req/month). The connector # refuses to call Exa once either cap is reached. Monthly count persists in # output/state/exa_usage.json and resets at the start of each calendar month. EXA_MONTHLY_REQUEST_CAP=1000 EXA_PER_RUN_REQUEST_CAP=25 EXA_RESULTS_PER_QUERY=5 EXA_LOOKBACK_DAYS=45 # Exa content category. "news" keeps out careers/marketing landing pages — the # biggest signal-quality lever. "" disables category filtering. EXA_CATEGORY=news # Scope to the account's own domain (1) or broad-web news about it (0, default). # With category=news, broad web finds actual coverage; domain scoping finds the # company's own newsroom. Override per run: loop --no-domain-filter / --domain-filter EXA_DOMAIN_FILTER=0 # Hosts to always exclude (comma-separated), e.g. talent.example.com,careers.example.com EXA_EXCLUDE_DOMAINS=