# =============================================================================
# Claw — Unified Environment Configuration
# =============================================================================
# Single source of truth for ALL services, databases, and infrastructure.
# Copy this file to .env and fill in your values.
#
# Used by:
#   - All docker-compose files (docker-compose*.yml) — ${VAR} interpolation
#   - All backend microservices — env_file: .env in compose
#   - Frontend — env_file: .env in compose
# =============================================================================

# --- General ---
NODE_ENV=development

# --- Hostname / Public URL ---
# The host your Claw instance is reachable at from a browser. Used to derive
# CORS_ORIGINS, NEXT_PUBLIC_APP_URL, /etc/hosts entry, and TLS cert SANs.
# Examples:
#   claw.local           — default local install (install-tls maps to 127.0.0.1)
#   claw.example.com     — public/intranet domain
#   192.168.1.50         — bare IP (TLS cert issued as an IP SAN)
# Whenever you change this, re-run scripts/install-tls.{sh,ps1} to re-issue
# the cert with the new SAN, then restart services.
CLAW_HOSTNAME=claw.local
CORS_ORIGINS=https://claw.local,https://claw.local:3000

# --- TLS / SSL (auto-managed by scripts/install-tls.{sh,ps1}) ---
# Container-relative paths — the mkcert leaf cert + root CA are bind-mounted
# from ../certs into every container at /certs. If the files don't exist,
# services fall back to plain HTTP (see resolveHttpsOptions in
# @claw/shared-utilities). NODE_EXTRA_CA_CERTS makes node's global fetch
# trust the mkcert root CA so inter-service HTTPS calls verify cleanly.
HTTPS_CERT_PATH=/certs/claw.crt
HTTPS_KEY_PATH=/certs/claw.key
NODE_EXTRA_CA_CERTS=/certs/rootCA.pem

# --- Rate Limiting ---
THROTTLE_TTL=60000
THROTTLE_LIMIT=100

# =============================================================================
# PostgreSQL Credentials (used by database containers)
# =============================================================================

# Auth
PG_AUTH_USER=claw
PG_AUTH_PASSWORD=claw_secret
PG_AUTH_DB=claw_auth
PG_AUTH_PORT=5441

# Chat
PG_CHAT_USER=claw
PG_CHAT_PASSWORD=claw_secret
PG_CHAT_DB=claw_chat
PG_CHAT_PORT=5442

# Connector
PG_CONNECTOR_USER=claw
PG_CONNECTOR_PASSWORD=claw_secret
PG_CONNECTOR_DB=claw_connectors
PG_CONNECTOR_PORT=5443

# Routing
PG_ROUTING_USER=claw
PG_ROUTING_PASSWORD=claw_secret
PG_ROUTING_DB=claw_routing
PG_ROUTING_PORT=5444

# Memory
PG_MEMORY_USER=claw
PG_MEMORY_PASSWORD=claw_secret
PG_MEMORY_DB=claw_memory
PG_MEMORY_PORT=5445

# Files
PG_FILES_USER=claw
PG_FILES_PASSWORD=claw_secret
PG_FILES_DB=claw_files
PG_FILES_PORT=5446

# Ollama
PG_OLLAMA_USER=claw
PG_OLLAMA_PASSWORD=claw_secret
PG_OLLAMA_DB=claw_ollama
PG_OLLAMA_PORT=5447

# Images
PG_IMAGES_USER=claw
PG_IMAGES_PASSWORD=claw_secret
PG_IMAGES_DB=claw_images
PG_IMAGES_PORT=5448

# File Generations
PG_FILE_GENERATIONS_USER=claw
PG_FILE_GENERATIONS_PASSWORD=claw_secret
PG_FILE_GENERATIONS_DB=claw_file_generations
PG_FILE_GENERATIONS_PORT=5449

# PostgreSQL — Workspace Service (port 5450)
PG_WORKSPACE_USER=claw
PG_WORKSPACE_PASSWORD=claw_secret
PG_WORKSPACE_DB=claw_workspace
PG_WORKSPACE_PORT=5450

# PostgreSQL — Research Service (port 5452)
PG_RESEARCH_USER=claw
PG_RESEARCH_PASSWORD=claw_secret
PG_RESEARCH_DB=claw_research
PG_RESEARCH_PORT=5452

# claw-llamacpp-service (Local Frontier — Phase 1)
PG_LLAMACPP_USER=claw
PG_LLAMACPP_PASSWORD=claw_secret
PG_LLAMACPP_DB=claw_llamacpp
PG_LLAMACPP_PORT=5440

# PostgreSQL — Agent Service (port 5451)
PG_AGENT_USER=claw
PG_AGENT_PASSWORD=claw_secret
PG_AGENT_DB=claw_agent
PG_AGENT_PORT=5451

# =============================================================================
# MongoDB Credentials (used by MongoDB container)
# =============================================================================
MONGO_USER=claw
MONGO_PASSWORD=claw_secret
MONGO_DB=claw_audit
MONGO_PORT=27018

# =============================================================================
# Redis
# =============================================================================
REDIS_URL=redis://redis:6379
REDIS_PORT=6380

# =============================================================================
# RabbitMQ
# =============================================================================
RABBITMQ_USER=claw
RABBITMQ_PASSWORD=claw_secret
RABBITMQ_URL=amqp://claw:claw_secret@rabbitmq:5672
RABBITMQ_PORT=5672
RABBITMQ_MANAGEMENT_PORT=15672

# =============================================================================
# JWT (shared across all services)
# =============================================================================
JWT_SECRET=claw-dev-jwt-secret-9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c
JWT_ACCESS_EXPIRY=15m
JWT_REFRESH_EXPIRY=7d

# =============================================================================
# Encryption (AES-256-GCM, 64 hex chars = 32 bytes)
# =============================================================================
# Generate with: openssl rand -hex 32
ENCRYPTION_KEY=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2

# =============================================================================
# Admin Seed (Auth Service)
# =============================================================================
ADMIN_EMAIL=admin@claw.local
ADMIN_USERNAME=claw-admin
ADMIN_PASSWORD=ClawAdmin123!

# System-role permissions reconciliation gate (auth-service boot seeder).
# 'false' (default): add-only. On first DB init the seeder grants the full
# SYSTEM_ROLE_SEED to ADMIN/USER; on later boots it only ADDS newly-introduced
# seed permissions and NEVER removes extras an admin granted via the UI (so
# Compare/Judge/etc. grants survive 'docker up'). Set 'true' to hard-reconcile
# both system roles back to the canonical seed (adds AND removes). Custom roles
# are never touched either way.
SEED_RECONCILE_PERMISSIONS=false

# =============================================================================
# Frontend (Next.js)
# =============================================================================
# All frontend traffic flows through nginx on https://claw.local (port 443).
# The frontend dev server still listens on :3000 internally for hot reload,
# but the browser only ever talks to nginx (which terminates TLS).
NEXT_PUBLIC_API_URL=
NEXT_PUBLIC_APP_NAME=Claw
NEXT_PUBLIC_APP_URL=https://claw.local
# Phase 8 UI transparency — dev-only Thread Context Inspector under each
# assistant message. Defaults to false so production builds never show it.
# Set to "true" locally to render the [debug] inspector button.
NEXT_PUBLIC_ROUTING_DEBUG_CONTEXT_INSPECTOR_ENABLED=false
FRONTEND_PORT=3000

# =============================================================================
# Ollama (Local AI Runtime)
# =============================================================================
OLLAMA_BASE_URL=http://ollama:11434
OLLAMA_ROUTER_MODEL=qwen3:1.7b
OLLAMA_ROUTER_TIMEOUT_MS=10000
ROUTER_COMPACT_PROMPT=true
OLLAMA_GENERATE_TIMEOUT_MS=300000
OLLAMA_KEEP_ALIVE=-1m
OLLAMA_MAX_LOADED_MODELS=2
OLLAMA_NUM_PARALLEL=1
OLLAMA_FLASH_ATTENTION=1
OLLAMA_KV_CACHE_TYPE=q8_0
MEMORY_EXTRACTION_MODEL=AUTO

# --- Ollama Cloud agentic tool-loop caps ---
# Bounds the runaway agentic tool loop used by deepseek-v4-pro / kimi-k2 /
# glm-5.1. When EITHER cap is hit the chat-service issues one final
# tool-less POST forcing the model to synthesize an answer from already
# gathered evidence (`metadata.toolTranscript.gracefullyWrapped=true`).
# Defaults raised 10 → 50 (2026-05-31) so deeper research chains complete
# without prematurely clipping into an error string.
OLLAMA_TOOL_LOOP_MAX_ITERATIONS=50
OLLAMA_TOOL_LOOP_TOTAL_TIMEOUT_MS=600000

# --- Local-only vision attachment policy (Slice B) ---
# When the active routing mode is LOCAL_ONLY or PRIVACY_FIRST and no local
# Ollama model with vision capability is installed (llava, bakllava, moondream,
# minicpm-v, llama3.2-vision, *-vision, *-multimodal), image attachments are
# dropped from the prompt and the user is warned via the
# `chat.localOnly.imagesDropped` i18n key. Set to `true` to forward images
# anyway (the local text-only model will silently ignore them).
ALLOW_LOCAL_ONLY_ATTACHMENTS_WITHOUT_VISION=false
# Max time (ms) to wait for the per-model vision-capability probe against the
# ollama-service registry before falling back to the "no vision model" path.
LOCAL_VISION_MODEL_DETECTION_TIMEOUT_MS=3000

# =============================================================================
# Memory + Context V2 Flagship
# =============================================================================
# Master flag for the V2 control center; v1 endpoints stay live regardless.
MEMORY_V2_ENABLED=true
CONTEXT_V2_ENABLED=true
RETRIEVAL_V2_ENABLED=true
# Ollama models that back the V2 sensitivity and embedding subsystems.
MEMORY_SENSITIVITY_MODEL=gemma3:4b
MEMORY_EMBEDDING_MODEL=nomic-embed-text
CONTEXT_EMBEDDING_MODEL=nomic-embed-text
CONTEXT_COMPRESSION_MODEL=gemma3:4b
# Per-user defaults for the suggestion queue (auto-approve cut-off) and retention sweep.
MEMORY_AUTO_APPROVE_DEFAULT=0.85
MEMORY_RETENTION_SWEEP_INTERVAL_MS=3600000
MEMORY_SUGGESTION_TTL_DAYS=30
CONTEXT_VERSION_RETENTION_COUNT=20
CONTEXT_TOKEN_ESTIMATOR_MODE=char/4
# Retrieval budgets used by `/internal/memories/retrieve`.
RETRIEVAL_MEMORY_SEMANTIC_BUDGET=5
RETRIEVAL_CONTEXT_SEMANTIC_BUDGET=12
RETRIEVAL_TOKEN_GUARD_PCT=0.4

# =============================================================================
# File Service
# =============================================================================
FILE_STORAGE_PATH=/data/files

# --- File retention (Slice C foundation 3) ---
# How long uploaded files live before the retention sweeper removes them and
# their chunks. A nightly cron scans for expired rows and deletes them in
# batches. Set FILE_RETENTION_DAYS to 0 to disable expiry (files live forever).
FILE_RETENTION_DAYS=30
# Cron expression (5-field, server local time) for the retention sweep job.
# Default = every day at 02:00.
FILE_RETENTION_SWEEP_CRON='0 2 * * *'
# Max number of expired files removed per sweep tick — keeps DB locks short.
FILE_RETENTION_SWEEP_BATCH_LIMIT=100

# --- ZIP archive expansion guardrails (Slice C foundation 3) ---
# Hard caps for any incoming .zip / .docx / .xlsx (ZIP-based) upload. The
# file-service validates archives against these limits BEFORE extraction to
# block ZIP bombs and resource exhaustion. Limits are enforced in this order:
#   1. nesting depth      → ZIP_MAX_NESTING_DEPTH
#   2. entry count        → ZIP_MAX_ENTRY_COUNT
#   3. compression ratio  → ZIP_COMPRESSION_RATIO_THRESHOLD (uncompressed/compressed)
#   4. extracted size     → ZIP_MAX_EXTRACTED_SIZE_MB
# Any violation aborts the upload and surfaces the i18n key
# `files.zip.bombRejected` to the user. Extraction happens inside
# ZIP_TEMP_EXTRACTION_PATH which MUST be a tmpfs/ramdisk in production.
ZIP_MAX_EXTRACTED_SIZE_MB=500
ZIP_MAX_ENTRY_COUNT=10000
ZIP_MAX_NESTING_DEPTH=5
ZIP_COMPRESSION_RATIO_THRESHOLD=1000
ZIP_TEMP_EXTRACTION_PATH=/tmp/claw-zip-extraction

# =============================================================================
# Image Generation (Stable Diffusion + ComfyUI)
# =============================================================================
STABLE_DIFFUSION_URL=http://stable-diffusion:7860
# Stable Diffusion WebUI progress polling (PR3 — runtime-progress)
# Poll interval in ms; clamped to a 300ms safe minimum at the adapter level.
CLAW_IMAGE_PROGRESS_POLL_INTERVAL_MS=1000
# When true, ask SD WebUI for live preview frames (current_image). Off by
# default — preview frames are large and cause throughput drops on weak GPUs.
CLAW_IMAGE_PROGRESS_PREVIEW_ENABLED=false
COMFYUI_BASE_URL=http://comfyui:8188
COMFYUI_PORT=8188
# Path inside the ollama-service container where comfyui-models-data is
# mounted read-write. Catalog pulls for COMFYUI runtime entries stream the
# HuggingFace file into <COMFYUI_MODELS_PATH>/<modelType>/<filename>; the
# claw-comfyui container mounts the same volume at /opt/ComfyUI/models.
COMFYUI_MODELS_PATH=/var/lib/claw/comfyui-models

# =============================================================================
# Local Model Auto-Pull (space-separated Ollama model names)
# =============================================================================
AUTO_PULL_MODELS=qwen3:1.7b

# =============================================================================
# Dynamic Ollama Model Discovery
# =============================================================================
DISCOVERY_AUTO_REFRESH_ENABLED=true
DISCOVERY_MAX_RESULTS_PER_SOURCE=50
DISCOVERY_AUTO_APPROVE_CONFIDENCE=0.85

# =============================================================================
# Security — ClamAV Antivirus
# =============================================================================
CLAMAV_HOST=clamav
CLAMAV_PORT=3310
CLAMAV_ENABLED=true

# Stream 22 — Service-to-service auth (file-service /upload-internal + /download-internal)
# Generate a random 64-char token in production: openssl rand -hex 32
INTER_SERVICE_AUTH_TOKEN=change-me-inter-service-token-32-chars-min-aaaaaaaaaa
# Stream 22 — Gmail HTML rendering + attachments
WORKSPACE_GMAIL_FETCH_ATTACHMENTS=true
WORKSPACE_GMAIL_MAX_ATTACHMENT_BYTES=26214400

# =============================================================================
# Inter-Service URLs (Docker hostnames)
# =============================================================================
# All inter-service hops go over HTTPS. NODE_EXTRA_CA_CERTS (set above)
# tells node's fetch/https to trust the mkcert root CA. Ports unchanged.
OLLAMA_SERVICE_URL=https://ollama-service:4008
CONNECTOR_SERVICE_URL=https://connector-service:4003
AUTH_SERVICE_URL=https://auth-service:4001
CHAT_SERVICE_URL=https://chat-service:4002
ROUTING_SERVICE_URL=https://routing-service:4004
MEMORY_SERVICE_URL=https://memory-service:4005
FILE_SERVICE_URL=https://file-service:4006
AUDIT_SERVICE_URL=https://audit-service:4007
CLIENT_LOGS_SERVICE_URL=https://client-logs-service:4010
SERVER_LOGS_SERVICE_URL=https://server-logs-service:4011
IMAGE_SERVICE_URL=https://image-service:4012
FILE_GENERATION_SERVICE_URL=https://file-generation-service:4013
WORKSPACE_SERVICE_URL=https://workspace-service:4014
RESEARCH_SERVICE_URL=https://research-service:4016
AGENT_SERVICE_URL=https://agent-service:4015
LLAMACPP_SERVICE_URL=https://llamacpp-service:4017

# =============================================================================
# Per-Service Ports
# =============================================================================
AUTH_PORT=4001
CHAT_PORT=4002
CONNECTOR_PORT=4003
ROUTING_PORT=4004
MEMORY_PORT=4005
FILES_PORT=4006
AUDIT_PORT=4007
OLLAMA_PORT=4008
HEALTH_PORT=4009
CLIENT_LOGS_PORT=4010
SERVER_LOGS_PORT=4011
IMAGE_PORT=4012
FILE_GENERATION_PORT=4013
WORKSPACE_PORT=4014
RESEARCH_PORT=4016
AGENT_PORT=4015
LLAMACPP_PORT=4017

# Workspace scheduled sync (Stream 01 Phase 5)
# Master kill-switch for the background scheduler. When false only manual
# "Sync now" works (legacy behavior).
WORKSPACE_SCHEDULER_ENABLED=true
# How often the scheduler wakes up to check per-connector eligibility.
# Cadence is per-connector via SyncCadenceDefault + WorkspaceConnector.syncIntervalSeconds.
WORKSPACE_SCHEDULER_TICK_CRON=*/30 * * * * *
# Stale-detector sweep cron. Flips connectors whose lastSyncAt is > N × cadence ago.
WORKSPACE_SYNC_STALE_DETECTOR_CRON=*/60 * * * * *
WORKSPACE_SYNC_STALE_MULTIPLIER=3
# Fallback interval when no SyncCadenceDefault row exists yet for a provider.
WORKSPACE_SYNC_DEFAULT_INTERVAL_SECONDS=600
# Concurrency caps — global across all connectors; per-provider; per-connector.
WORKSPACE_SYNC_MAX_CONCURRENT_GLOBAL=20
WORKSPACE_SYNC_MAX_CONCURRENT_PER_PROVIDER=5
WORKSPACE_SYNC_MAX_CONCURRENT_PER_CONNECTOR=1
# Retry policy for sync-run failures. Delay = baseMs * 2^attempt + rand(0, jitterMs).
WORKSPACE_SYNC_RETRY_MAX_ATTEMPTS=3
WORKSPACE_SYNC_RETRY_BASE_MS=1000
WORKSPACE_SYNC_RETRY_JITTER_MS=500
# DLQ routing-key prefix for terminal failures. Full key = "<prefix>.<provider_lowercase>".
WORKSPACE_SYNC_DLQ_ROUTING_PREFIX=workspace.sync.dlq

# Desktop Agent auth (Phase A: magic-link pairing + device-code + refresh rotation)
AGENT_ACCESS_TTL_SECONDS=900
AGENT_REFRESH_TTL_DAYS=30
AGENT_PAIRING_TTL_SECONDS=120
AGENT_DEVICE_CODE_TTL_SECONDS=900
AGENT_REFRESH_GRACE_SECONDS=15

# Desktop Agent capability framework (Stream 10 + V2 Stream 01 closeout)
# CAPABILITY_DEPRECATED_TERMINAL_COMMAND_DUAL_WRITE controls whether
# CommandRiskService also calls CapabilityRiskService for the
# terminal-command soak window. Default-on while the divergence-count
# (GET /api/v1/agent/capability/dual-write-status) is non-zero; flip to
# `false` once the divergence rate has been zero for 7 consecutive days.
# See docs/15-ai-context/desktop-agent-dual-write-retirement.md for the
# retirement plan and post-flip rollback instructions.
CAPABILITY_DEPRECATED_TERMINAL_COMMAND_DUAL_WRITE=true

# OAuth2 App Credentials (for workspace connectors)
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
SLACK_CLIENT_ID=your-slack-client-id
SLACK_CLIENT_SECRET=your-slack-client-secret
JIRA_CLIENT_ID=your-jira-client-id
JIRA_CLIENT_SECRET=your-jira-client-secret
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

# Workspace webhook signing secrets (per provider). Used by claw-workspace-service
# to verify inbound webhook payloads (HMAC for GitHub/GitLab/Bitbucket/Figma/Jira,
# Slack v0 signature for Slack). Generate a random 64-char hex token per provider:
#   openssl rand -hex 32
# scripts/install.{sh,ps1} auto-generates these on first install.
GITHUB_WEBHOOK_SECRET=change-me-github-webhook-secret-32-chars-min
GITLAB_WEBHOOK_SECRET=change-me-gitlab-webhook-secret-32-chars-min
SLACK_SIGNING_SECRET=change-me-slack-signing-secret-32-chars-min
JIRA_WEBHOOK_SECRET=change-me-jira-webhook-secret-32-chars-min
BITBUCKET_WEBHOOK_SECRET=change-me-bitbucket-webhook-secret-32-chars-min
FIGMA_WEBHOOK_SECRET=change-me-figma-webhook-secret-32-chars-min

# Workspace AI Actions
# Model selection is dynamic — resolved at runtime from connected connectors
# (claw-connector-service) and installed Ollama models (claw-ollama-service).
# DO NOT pin a specific local model here; the system resolves the best
# available model per action kind based on what's actually present.
AI_ACTION_REQUEST_TIMEOUT_MS=300000
AI_ACTION_MODEL_RESOLVER_TTL_SECONDS=300

# Workspace AI Action Approval Engine (Stream 10)
AI_ACTION_QUEUE_EXPIRY_HOURS=24
AI_ACTION_RISK_AUTO_APPROVE_MAX=30
AI_ACTION_QUEUE_EXPIRY_SWEEP_CRON=0 */15 * * * *
AI_ACTION_QUEUE_EXPIRY_BATCH_LIMIT=100

# =============================================================================
# Per-Service Database URLs
# =============================================================================
AUTH_DATABASE_URL=postgresql://claw:claw_secret@pg-auth:5432/claw_auth?schema=public
CHAT_DATABASE_URL=postgresql://claw:claw_secret@pg-chat:5432/claw_chat?schema=public
CONNECTOR_DATABASE_URL=postgresql://claw:claw_secret@pg-connector:5432/claw_connectors?schema=public
ROUTING_DATABASE_URL=postgresql://claw:claw_secret@pg-routing:5432/claw_routing?schema=public
MEMORY_DATABASE_URL=postgresql://claw:claw_secret@pg-memory:5432/claw_memory?schema=public
FILES_DATABASE_URL=postgresql://claw:claw_secret@pg-files:5432/claw_files?schema=public
OLLAMA_DATABASE_URL=postgresql://claw:claw_secret@pg-ollama:5432/claw_ollama?schema=public
IMAGE_DATABASE_URL=postgresql://claw:claw_secret@pg-images:5432/claw_images?schema=public
FILE_GENERATION_DATABASE_URL=postgresql://claw:claw_secret@pg-file-generations:5432/claw_file_generations?schema=public
WORKSPACE_DATABASE_URL=postgresql://claw:claw_secret@pg-workspace:5432/claw_workspace?schema=public
RESEARCH_DATABASE_URL=postgresql://claw:claw_secret@pg-research:5432/claw_research?schema=public
AGENT_DATABASE_URL=postgresql://claw:claw_secret@pg-agent:5432/claw_agent?schema=public
LLAMACPP_DATABASE_URL=postgresql://claw:claw_secret@pg-llamacpp:5432/claw_llamacpp?schema=public

AUDIT_MONGODB_URI=mongodb://claw:claw_secret@mongodb:27017/claw_audit?authSource=admin
CLIENT_LOGS_MONGODB_URI=mongodb://claw:claw_secret@mongodb:27017/claw_client_logs?authSource=admin
SERVER_LOGS_MONGODB_URI=mongodb://claw:claw_secret@mongodb:27017/claw_server_logs?authSource=admin

# =============================================================================
# claw-llamacpp-service (Local Frontier LLM runtime)
# =============================================================================
# Must match the `llamacpp-data` named-volume mount in
# docker-compose.dev.services.yml + docker-compose.prod.services.yml. Using a
# path inside the volume makes the auto-installed binary + downloaded model
# weights survive `docker compose up -d --build` rebuilds.
LLAMACPP_DATA_PATH=/var/lib/claw/llamacpp
LLAMACPP_BINARY_VERSION=b9095
LLAMACPP_GPU_BACKEND=auto
LLAMACPP_DEFAULT_CTX_SIZE=32768
LLAMACPP_AUTO_INSTALL_BINARY=true
LLAMACPP_FORCE_PINNED_BINARY=false
LLAMACPP_PREFLIGHT_OVERRIDE_ALLOWED=true
LLAMACPP_LOAD_TIMEOUT_MS=600000
LLAMACPP_BIND_HOST=127.0.0.1
LLAMACPP_PROCESS_PORT_MIN=48500
LLAMACPP_PROCESS_PORT_MAX=48999
HUGGINGFACE_TOKEN=
HUGGINGFACE_API_BASE=https://huggingface.co

# =============================================================================
# claw-workspace-service — runtime gates (Phase E close-out, 2026-05-02)
# =============================================================================
# Stream 13.3 — per-event-type budget cap on the suggestion factory (per process).
WORKSPACE_SUGGESTION_FACTORY_RATE_PER_HOUR=100
# Stream 11.4 — per-connector webhook rate limit (per process, sliding window).
WEBHOOK_CONNECTOR_REQUESTS_PER_MINUTE=60
# Stream 12.2 — Gmail INBOX_REPLY auto-suggest collector cron + lookback.
AUTO_SUGGEST_INBOX_REPLY_CRON=0 */15 * * * *
AUTO_SUGGEST_INBOX_REPLY_LOOKBACK_HOURS=48

# =============================================================================
# Desktop Agent — native capability tooling (populated by install-agent-tooling)
# =============================================================================
# Set automatically by scripts/install-agent-tooling.{sh,ps1} when the binaries
# are downloaded. Leave blank to use whatever's on PATH.
#
#   AUDIO.TRANSCRIBE — whisper.cpp + ggml model
WHISPER_CLI_PATH=
WHISPER_MODEL_PATH=
#   AUDIO.SYNTHESIZE — Piper binary
PIPER_BIN_PATH=
#   SCREEN.OCR — tesseract is auto-detected on PATH; override here if needed
TESSERACT_BIN_PATH=
#   BROWSER class — Playwright honors its own browsers cache; uncomment to override
# PLAYWRIGHT_BROWSERS_PATH=

# =============================================================================
# Semantic Router Flagship — phased rollout flags
# See docs/03-architecture/semantic-router-flagship-plan.md
# Defaults preserve the current v1 hot path; flip to advance a phase.
# =============================================================================
# Phase 2 — Semantic Intent Analyzer
ROUTING_SEMANTIC_ANALYZER_ENABLED=false
ROUTING_SEMANTIC_ANALYZER_USE_FOR_ROUTING=false
# Phase 4 — AI Route Planner
ROUTING_AI_ROUTE_PLANNER_ENABLED=false
ROUTING_AI_ROUTE_PLANNER_USE_FOR_ROUTING=false
ROUTING_V2_CANARY_PERCENT=0
# Phase 1 — Thread Context + Follow-up detection (on by default)
ROUTING_THREAD_CONTEXT_INJECTION_ENABLED=true
ROUTING_FOLLOW_UP_DETECTION_ENABLED=true
# Phase 5 — Formal 3-attempt Fallback Executor
ROUTING_FALLBACK_ATTEMPTS_ENABLED=false
ROUTING_MAX_FALLBACK_ATTEMPTS=3
# Phase 7 — Auto-judge for high-risk routes
ROUTING_JUDGE_HIGH_RISK_ENABLED=false
# Phase 9 — Learning loop integrated into scoring (live learnedSuccess)
ROUTING_LEARNING_LOOP_INTEGRATED_ENABLED=false
# Phase 8 — Dev-only context inspector panel
ROUTING_DEBUG_CONTEXT_INSPECTOR_ENABLED=false

# =============================================================================
# Compare / Judge / Critic File Attachments — Slice D foundation 3
# (Anthropic native PDF + Gemini Files API + OCR pipeline)
# See docs/03-architecture/compare-file-attachments.md (Slice D close-out)
# All flags default OFF / safe-mode so deployments opt-in.
# =============================================================================
# Anthropic native PDF input. When true, the anthropic.adapter forwards PDF
# bytes as a `document` content part instead of falling through to extracted-
# text. Requires anthropic-version header >= 2024-06-01 (bumped centrally).
ENABLE_ANTHROPIC_NATIVE_PDF=false

# Gemini Files API. When true, large attachments (>= threshold) are uploaded
# to Gemini's Files API and referenced by URI instead of inlined as base64.
# Files API entries live for 48h on Google's side; we cache the URI for half
# that window to be safe.
ENABLE_GEMINI_FILES_API=false
GEMINI_FILES_API_SIZE_THRESHOLD_BYTES=20000000      # 20 MB — files >= this use the Files API
GEMINI_FILES_API_TIMEOUT_MS=60000                   # per-upload timeout
GEMINI_FILES_API_CACHE_ENABLED=true                 # in-process URI cache keyed by (fileId, model)
GEMINI_FILES_API_TTL_MINUTES=1440                   # 24h cache TTL (Gemini side keeps for 48h)
GEMINI_CONCURRENT_UPLOADS_LIMIT=3                   # max parallel Files API uploads per chat-service

# OCR for image attachments and scanned PDFs (file-service tesseract worker).
# When true and a file is detected as a scanned PDF (extracted text length <
# SCANNED_PDF_CHAR_THRESHOLD) or an image bound for a text-only model,
# tesseract is invoked to extract text. OCR results are cached on the File row.
OCR_ENABLED=false
OCR_TIMEOUT_MS=30000                                 # per-file OCR timeout (tesseract worker)
OCR_CONFIDENCE_MIN=0.5                               # below this we mark the OCR result as low-confidence
OCR_LANGUAGE=eng                                     # tesseract language pack (e.g. eng, eng+ara)
OCR_WORKER_THREADS=2                                 # parallel tesseract workers per file-service container
SCANNED_PDF_CHAR_THRESHOLD=100                       # extracted-text char count below which a PDF is treated as scanned and routed to OCR
