{"repo": "als-apg/osprey", "files": {"src/osprey/templates/project/config.yml.j2": {"905f38b2c3830bc74e6b6953b43eb8c383ae628f": {"t": 1785746861.0457718, "n": 16750, "h": "9c9d1c71a32ec608", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services launched by `osprey deploy up`.\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything. For a GENERIC OTLP backend\n # instead, set backend: generic and endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync (single source of truth).\n # `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env; the\n # ${VAR:-default} fallbacks keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category. (If you rebind the store off localhost, change the default\n # password first — these records include raw API bodies.)\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL CONFIGURATION\n# ============================================================\n# Browser-based Web Terminal UI. Independent of the CLI's `cli.theme` below\n# (console-only) — the two never share a value.\n\nweb:\n # Default web UI theme: a theme FAMILY (e.g. \"osprey\", \"high-contrast\") or\n # a concrete theme id (e.g. \"high-contrast-light\") to pin a specific mode.\n # A user's in-browser choice (switcher selection, ?theme=, localStorage)\n # always overrides this default. An unknown value falls back to \"osprey\".\n theme: osprey\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "7fd858b80c0c8f2ce161eee0ca48a19f82917b0c": {"t": 1785768109.0290139, "n": 25497, "h": "1d90cf811fd7b3a0", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\n# Facility identity — one block, two keys:\n# name — display name woven into the agent prompts (CLAUDE.md, subagents)\n# and the web-terminal landing page. Falls back to the older\n# top-level `facility_name`, then to the project name.\n# prefix — short abbreviation the deployed web stack uses for container\n# names (`-nginx`, `-web-`); must be a\n# non-empty valid Docker name start ([a-zA-Z0-9]).\n# facility:\n# name: \"Example Research Facility\"\n# prefix: ex\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services launched by `osprey deploy up`.\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware for `execute`\n# only — under `selective` every other tool is treated as `always`), skip (no\n# approval).\n# Scope: applied by the osprey_approval PreToolUse hook, so these policies reach\n# only tools whose matcher runs that hook. Tools with no approval hook (the\n# health check, channel-finder queries, most workspace tools) are gated by the\n# rendered settings.json permissions instead.\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed for hook-wired tools not listed below\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Shipped OFF here (the readers default to false when the section is absent).\n# Turning it on drives three things, not just a debug print: one line per hook\n# call to stderr; an append-only /.claude/hooks/hook_debug.jsonl that\n# is never rotated or size-capped (prune it yourself on a long-lived\n# deployment); and the web terminal's Safety-panel hook-activity feed, which\n# reads that file and shows nothing new while this is false. The\n# OSPREY_HOOK_DEBUG environment variable forces it on regardless of this key,\n# and the Safety panel's own toggle writes this key back into config.yml —\n# creating the section even though the template ships none.\n# hooks:\n# debug: true\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Subdirectory names within the agent data directory above.\nfile_paths:\n api_calls_dir: api_calls\n registry_exports_dir: registry_exports\n\n# Raw provider request/response dumps, written into the api_calls_dir above.\n# Off unless `save_all` is true; a debugging aid, not the telemetry pipeline\n# (that is claude_code.telemetry). The files are unrotated and contain full\n# request bodies, so turn it off again when you are done.\n# development:\n# api_calls:\n# save_all: true # default false — nothing is written without this\n# latest_only: false # DEFAULT true: one `_latest.txt` per call\n# # site, overwritten each time. false keeps every\n# # call under a timestamped name instead.\n# include_stack_trace: true # default false — record the full call stack\n\n# osprey:panel-port artifact\n# WORKSPACE tab of `osprey web` — the artifact gallery, served by its own web\n# server. No section ships here, so it runs at its defaults: it is a universal\n# panel — launched at `osprey web` startup whatever panels are configured, and\n# on the first artifact save in headless runs — and it binds 127.0.0.1:8086.\n# Port resolution is the one shared function every launcher and URL builder\n# uses: OSPREY_ARTIFACT_SERVER_PORT wins (multi-user deployments\n# export it per user), then `port` below, then the built-in 8086. `host` has no\n# env override. Uncomment to move it, or set `auto_launch: false` to switch it\n# off — the tab is then reported unavailable rather than serving a dead iframe.\n# artifact_server:\n# host: \"127.0.0.1\"\n# port: 8086\n# auto_launch: true\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n # How the connector confirms a write took effect. Applies only when the caller does\n # not name a level: osprey.runtime.write_channel scripts and direct connector calls.\n # The channel_write MCP tool, multi-channel writes, and Bluesky device writes pass an\n # explicit level (\"callback\"); for those, only a per-channel \"verification\" entry in\n # channel_limits.json changes the level, and only on single-channel tool writes.\n # A failed verification is not a hard stop: the channel_write MCP tool reports it\n # (verification.verified: false), osprey.runtime.write_channel discards it (a readback\n # mismatch is logged at debug level only), and only write_channel_checked raises (scan\n # plans, which write through the Bluesky bridge's connector-backed devices). A write\n # the CA layer could not deliver at all - for example a PV that never connects -\n # sets success: false, which does raise from osprey.runtime.write_channel.\n write_verification:\n default_level: \"callback\" # Options: none | callback | readback (\"none\" = no verification)\n default_tolerance_percent: 0.1 # \"readback\" only; per-channel override in channel_limits.json\n\n # Connector-specific configuration\n connector:\n mock:\n response_delay_ms: 10 # Simulated per-operation response delay\n noise_level: 0.01 # Relative noise on generated values (0-1)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# ARCHIVER CONFIGURATION\n# ============================================================\n# Backs the archiver_read tool with historical channel data.\n\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance (add an `epics_archiver.url`)\n type: mock_archiver # Change to 'epics_archiver' for production\n # epics_archiver:\n # url: https://archiver.example.com # Replace with your archiver address\n # timeout: 60\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + the provider's own\n # model IDs from api.providers[name].models)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc — grpc requires an explicit `endpoint:` below; it is refused against the auto-derived openobserve endpoint (HTTP only)\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything. For a GENERIC OTLP backend\n # instead, set backend: generic and endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync (single source of truth).\n # `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env; the\n # ${VAR:-default} fallbacks keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category. (If you rebind the store off localhost, change the default\n # password first — these records include raw API bodies.)\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL CONFIGURATION\n# ============================================================\n# Two sections, both read, easy to confuse: `web_terminal:` is the terminal\n# PROCESS (bind address, shell, watcher), `web:` is the browser UI it renders\n# (theme, panel tabs). `web_terminal:` has never shipped in a template, so\n# every key below is at its default.\n# web_terminal:\n# host: \"127.0.0.1\" # Bind address. A deployment that DECLARES\n# # OSPREY_TERMINAL_BIND_HOST — the multi-user compose\n# # sets it on every per-user container so nginx is the\n# # only off-host path — overrides both this key and\n# # --host, so this cannot punch through that chokepoint.\n# port: 8087 # Likewise outranked by OSPREY_TERMINAL_WEB_PORT.\n# shell: /path/to/claude\n# # argv spawned in every PTY. Setting it REPLACES the\n# # launcher, so it also defeats the\n# # claude_code.cli_version pin above.\n# max_background_sessions: 5\n# watch_dir: ./_agent_data\n# # Directory the Workspace file watcher follows. Unset,\n# # it falls back to ./_agent_data relative to the\n# # launch directory — it does NOT read\n# # agent_data.base_dir, so if you moved that, set this\n# # to match.\n\n# Browser-based Web Terminal UI. Independent of the CLI's `cli.theme` below\n# (console-only) — the two never share a value.\n\nweb:\n # Default web UI theme: a theme FAMILY (e.g. \"osprey\", \"high-contrast\") or\n # a concrete theme id (e.g. \"high-contrast-light\") to pin a specific mode.\n # A user's in-browser choice (switcher selection, ?theme=, localStorage)\n # always overrides this default. An unknown value falls back to \"osprey\".\n theme: osprey\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# ============================================================\n# API PROVIDERS\n# ============================================================\n# Each entry takes `api_key`, `base_url`, and a `models` tier map (haiku /\n# sonnet / opus -> the model IDs that provider serves).\n#\n# `base_url` is the endpoint the agent itself uses. For the built-in\n# claude_code.provider names (anthropic, cborg, als-apg) a value set here\n# overrides the URL the framework ships; omit it to keep the built-in one. A\n# trailing `/v1` is stripped for the agent (it appends `/v1/messages`) but kept\n# for the OpenAI translation proxy, so leave it on gateways that need it.\n#\n# A fourth key is read but shipped by no template:\n#\n# api.providers..default_model_id: \n#\n# It is consulted only when framework code calls the completion API with\n# neither a model config nor an explicit model_id; without it, such a call\n# fails with \"Model ID required\". It does not pick the agent's model (that is\n# claude_code.default_model) and it never overrides the tier map above.\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n models: # Gateway serves no Opus-class model; o3-mini is the strongest\n haiku: gpt-4o-mini\n sonnet: gpt-4o\n opus: o3-mini\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n models: # GPT-5 sizes mapped onto the standard tiers\n haiku: gpt-5-nano\n sonnet: gpt-5-mini\n opus: gpt-5\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n models: # Gemini 2.5 sizes mapped onto the standard tiers\n haiku: gemini-2.5-flash-lite\n sonnet: gemini-2.5-flash\n opus: gemini-2.5-pro\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n models: # Local models — `ollama pull` each one before use\n haiku: mistral:7b\n sonnet: gpt-oss:20b\n opus: gpt-oss:120b\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n models: # Argo spells its model IDs without punctuation\n haiku: claudehaiku45\n sonnet: claudesonnet45\n opus: claudeopus41\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # api_protocol: anthropic # Keep this line for an Anthropic-native\n # # gateway. Only anthropic, cborg and\n # # als-apg are recognised natively by\n # # name; every other provider is assumed\n # # to speak OpenAI protocol and is routed\n # # through the translation proxy to\n # # /chat/completions. Copying this block\n # # without it silently adds that hop.\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n"}, "14cd3817731d9423ff039640d872bf31555e400a": {"t": 1785768110.3049562, "n": 16750, "h": "9c9d1c71a32ec608", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services launched by `osprey deploy up`.\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything. For a GENERIC OTLP backend\n # instead, set backend: generic and endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync (single source of truth).\n # `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env; the\n # ${VAR:-default} fallbacks keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category. (If you rebind the store off localhost, change the default\n # password first — these records include raw API bodies.)\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL CONFIGURATION\n# ============================================================\n# Browser-based Web Terminal UI. Independent of the CLI's `cli.theme` below\n# (console-only) — the two never share a value.\n\nweb:\n # Default web UI theme: a theme FAMILY (e.g. \"osprey\", \"high-contrast\") or\n # a concrete theme id (e.g. \"high-contrast-light\") to pin a specific mode.\n # A user's in-browser choice (switcher selection, ?theme=, localStorage)\n # always overrides this default. An unknown value falls back to \"osprey\".\n theme: osprey\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "2cb5bd5cd3969b8efd700a4d1be0a80d7a5f0685": {"t": 1785768111.5496712, "n": 16798, "h": "e886482efa71b3c0", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services launched by `osprey deploy up`.\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything. For a GENERIC OTLP backend\n # instead, set backend: generic and endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync (single source of truth).\n # `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env; the\n # ${VAR:-default} fallbacks keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category. (If you rebind the store off localhost, change the default\n # password first — these records include raw API bodies.)\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL CONFIGURATION\n# ============================================================\n# Browser-based Web Terminal UI. Independent of the CLI's `cli.theme` below\n# (console-only) — the two never share a value.\n\nweb:\n # Default web UI theme: a theme FAMILY (e.g. \"osprey\", \"high-contrast\") or\n # a concrete theme id (e.g. \"high-contrast-light\") to pin a specific mode.\n # A user's in-browser choice (switcher selection, ?theme=, localStorage)\n # always overrides this default. An unknown value falls back to \"osprey\".\n theme: osprey\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "95f7eb6363d1f7bab978b6affb80b1ced6e0bd45": {"t": 1785768113.0509803, "n": 15921, "h": "854c6c264dde66ce", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services launched by `osprey deploy up`.\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything. For a GENERIC OTLP backend\n # instead, set backend: generic and endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync (single source of truth).\n # `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env; the\n # ${VAR:-default} fallbacks keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category. (If you rebind the store off localhost, change the default\n # password first — these records include raw API bodies.)\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL CONFIGURATION\n# ============================================================\n# Browser-based Web Terminal UI. Independent of the CLI's `cli.theme` below\n# (console-only) — the two never share a value.\n\nweb:\n # Default web UI theme: a theme FAMILY (e.g. \"osprey\", \"high-contrast\") or\n # a concrete theme id (e.g. \"high-contrast-light\") to pin a specific mode.\n # A user's in-browser choice (switcher selection, ?theme=, localStorage)\n # always overrides this default. An unknown value falls back to \"osprey\".\n theme: osprey\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "986a688acd629d9a1d59859d9c9db1fa4854af7c": {"t": 1785768114.487415, "n": 15670, "h": "b1d41a3c070e5792", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services launched by `osprey deploy up`.\ndeployed_services:\n - openobserve\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything. For a GENERIC OTLP backend\n # instead, set backend: generic and endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync (single source of truth).\n # `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env; the\n # ${VAR:-default} fallbacks keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category. (If you rebind the store off localhost, change the default\n # password first — these records include raw API bodies.)\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL CONFIGURATION\n# ============================================================\n# Browser-based Web Terminal UI. Independent of the CLI's `cli.theme` below\n# (console-only) — the two never share a value.\n\nweb:\n # Default web UI theme: a theme FAMILY (e.g. \"osprey\", \"high-contrast\") or\n # a concrete theme id (e.g. \"high-contrast-light\") to pin a specific mode.\n # A user's in-browser choice (switcher selection, ?theme=, localStorage)\n # always overrides this default. An unknown value falls back to \"osprey\".\n theme: osprey\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "dbe4e4cfab5e206326d8d72b5c21f831726d5a71": {"t": 1785768115.7219572, "n": 12169, "h": "7c68609b7d02c051", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB TERMINAL CONFIGURATION\n# ============================================================\n# Browser-based Web Terminal UI. Independent of the CLI's `cli.theme` below\n# (console-only) — the two never share a value.\n\nweb:\n # Default web UI theme: a theme FAMILY (e.g. \"osprey\", \"high-contrast\") or\n # a concrete theme id (e.g. \"high-contrast-light\") to pin a specific mode.\n # A user's in-browser choice (switcher selection, ?theme=, localStorage)\n # always overrides this default. An unknown value falls back to \"osprey\".\n theme: osprey\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "755f6952bddc2afa80ded88d4a4b7614012ba432": {"t": 1785768116.9813347, "n": 11562, "h": "3fc6155fec1a2f97", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "b0cfc28b4f3f6ddc346b414c22fd9f10c828efcb": {"t": 1785768118.390198, "n": 11612, "h": "00d9ede7e31d29c4", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "94ca3f490c9d5995809b79de10f7b8ad5b056c62": {"t": 1785768119.6481895, "n": 11298, "h": "d92d71b8771190ee", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "e3a37576b30a19d67aa9645175e0f2032d435f09": {"t": 1785768120.8594744, "n": 11139, "h": "7cb208d9dcb641ef", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "3725d191dfe4dbd8f79579bb052f634190b2f753": {"t": 1785768122.160034, "n": 10546, "h": "a6e09fd8d063f1c2", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "7e8812c85349852ec09887563baeebb94aaae15a": {"t": 1785768123.711615, "n": 11136, "h": "1e522e4b488599a6", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "a1a8e7d08520254edf4aac2051be76a22f438eb7": {"t": 1785768125.0659847, "n": 10543, "h": "04e6bc8a4f673550", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "951af1a5a17830285f3054f06983b2d5ebfb6339": {"t": 1785768126.4608166, "n": 10098, "h": "829ee0b90d4e9587", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "870a3a0f2c57715ffe068049e8099b511e8f1281": {"t": 1785768127.7643645, "n": 10121, "h": "1a4dcd37a1aa19ac", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "f02dc049f80ef7b43f8c6d9bedb1918e70c3a902": {"t": 1785768129.200812, "n": 10139, "h": "bc402e6fff46e37b", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "a16e170558ca85fb793537e1530601e539f2c6b7": {"t": 1785768130.4673488, "n": 10871, "h": "37dfd0ef48e83d3c", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "d989f9568420e085eb689a52ebf8a1a5223ef8ee": {"t": 1785768131.8508623, "n": 10255, "h": "2a4518b8937f7887", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "ecd88510556185fb53bf05ad0c2b2b0bdc1c708b": {"t": 1785768133.1762478, "n": 10155, "h": "635c0d3976328909", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "79436c9bdfefef24553a9af37bc23771c0795176": {"t": 1785768134.5226266, "n": 10187, "h": "919bb11d0d09ae55", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "3889d894c3364763cd354b5aceb5265012a9144f": {"t": 1785768137.8994544, "n": 9910, "h": "52b87e63646d805d", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Disable framework agents:\n # agents:\n # literature-search:\n # enabled: false\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "f8c3ec234c80fccf53d295e53de651d354054977": {"t": 1785768140.8546839, "n": 10079, "h": "0292fcc2521d6414", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n capabilities:\n python_execution:\n enabled: true\n mode: \"control_writes\" # Options: disabled | control_writes | all_code\n memory:\n enabled: true\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Disable framework agents:\n # agents:\n # literature-search:\n # enabled: false\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "60840cb5e4fa6e44d51dc503664390a695cde294": {"t": 1785768146.8870475, "n": 10857, "h": "be737895f01d22ab", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n capabilities:\n python_execution:\n enabled: true\n mode: \"control_writes\" # Options: disabled | control_writes | all_code\n memory:\n enabled: true\n\n# Execution limits and safety controls\nexecution_control:\n # Agent control - Runtime behavior configuration\n agent_control:\n orchestration_mode: plan_first # Options: plan_first | react\n task_extraction_bypass_enabled: false # Skip LLM task extraction\n capability_selection_bypass_enabled: false # Skip LLM capability selection\n\n # Execution safety limits and control flow\n limits:\n max_reclassifications: 1 # Maximum times a task can be reclassified\n max_planning_attempts: 2 # Maximum number of planning attempts\n max_step_retries: 0 # Not used in Claude Code mode\n max_execution_time_seconds: 300 # Default timeout\n max_concurrent_classifications: 5 # Maximum concurrent LLM classification requests\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Disable framework agents:\n # agents:\n # literature-search:\n # enabled: false\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "bf4f040c3cd5e599a2d3530ba9b1e24e13f2576b": {"t": 1785768149.8979225, "n": 10734, "h": "2631d326cc8b5637", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n capabilities:\n python_execution:\n enabled: true\n mode: \"control_writes\" # Options: disabled | control_writes | all_code\n memory:\n enabled: true\n\n# Execution limits and safety controls\nexecution_control:\n # Agent control - Runtime behavior configuration\n agent_control:\n orchestration_mode: plan_first # Options: plan_first | react\n task_extraction_bypass_enabled: false # Skip LLM task extraction\n capability_selection_bypass_enabled: false # Skip LLM capability selection\n\n # Execution safety limits and control flow\n limits:\n max_reclassifications: 1 # Maximum times a task can be reclassified\n max_planning_attempts: 2 # Maximum number of planning attempts\n max_step_retries: 0 # Not used in Claude Code mode\n max_execution_time_seconds: 300 # Default timeout\n max_concurrent_classifications: 5 # Maximum concurrent LLM classification requests\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Disable framework agents:\n # agents:\n # literature-search:\n # enabled: false\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "e992058a3c80e05678adb3e578992d1d76522316": {"t": 1785768152.73377, "n": 10730, "h": "ab901a0e64e36b70", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n capabilities:\n python_execution:\n enabled: true\n mode: \"control_writes\" # Options: disabled | control_writes | all_code\n memory:\n enabled: true\n\n# Execution limits and safety controls\nexecution_control:\n # Agent control - Runtime behavior configuration\n agent_control:\n orchestration_mode: plan_first # Options: plan_first | react\n task_extraction_bypass_enabled: false # Skip LLM task extraction\n capability_selection_bypass_enabled: false # Skip LLM capability selection\n\n # Execution safety limits and control flow\n limits:\n max_reclassifications: 1 # Maximum times a task can be reclassified\n max_planning_attempts: 2 # Maximum number of planning attempts\n max_step_retries: 0 # Not used in Claude Code mode\n max_execution_time_seconds: 300 # Default timeout\n max_concurrent_classifications: 5 # Maximum concurrent LLM classification requests\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Disable framework agents:\n # agents:\n # literature-search:\n # enabled: false\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"cborg\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "388d0cb5c830ba49ef05b2b0b448d549dd17a091": {"t": 1785768156.3224273, "n": 10643, "h": "968481a3129e1154", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n capabilities:\n python_execution:\n enabled: true\n mode: \"control_writes\" # Options: disabled | control_writes | all_code\n memory:\n enabled: true\n\n# Execution limits and safety controls\nexecution_control:\n # Agent control - Runtime behavior configuration\n agent_control:\n orchestration_mode: plan_first # Options: plan_first | react\n task_extraction_bypass_enabled: false # Skip LLM task extraction\n capability_selection_bypass_enabled: false # Skip LLM capability selection\n\n # Execution safety limits and control flow\n limits:\n max_reclassifications: 1 # Maximum times a task can be reclassified\n max_planning_attempts: 2 # Maximum number of planning attempts\n max_step_retries: 0 # Not used in Claude Code mode\n max_execution_time_seconds: 300 # Default timeout\n max_concurrent_classifications: 5 # Maximum concurrent LLM classification requests\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Disable framework agents:\n # agents:\n # literature-search:\n # enabled: false\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"cborg\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "ec11f01380cd42a3f4247ab0d8b8dc3bfb3a2087": {"t": 1785768158.7067916, "n": 10776, "h": "4a43222d6d671ef4", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n capabilities:\n python_execution:\n enabled: true\n mode: \"control_writes\" # Options: disabled | control_writes | all_code\n memory:\n enabled: true\n\n# Execution limits and safety controls\nexecution_control:\n # Agent control - Runtime behavior configuration\n agent_control:\n orchestration_mode: plan_first # Options: plan_first | react\n task_extraction_bypass_enabled: false # Skip LLM task extraction\n capability_selection_bypass_enabled: false # Skip LLM capability selection\n\n # Execution safety limits and control flow\n limits:\n max_reclassifications: 1 # Maximum times a task can be reclassified\n max_planning_attempts: 2 # Maximum number of planning attempts\n max_step_retries: 0 # Not used in Claude Code mode\n max_execution_time_seconds: 300 # Default timeout\n max_concurrent_classifications: 5 # Maximum concurrent LLM classification requests\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# Python executor settings\npython_executor:\n max_generation_retries: 3\n max_execution_retries: 3\n execution_timeout_seconds: 600\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Disable framework agents:\n # agents:\n # literature-search:\n # enabled: false\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"cborg\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "32a8af7325450cc4fd713de04918ecec2a908021": {"t": 1785768161.6978302, "n": 10500, "h": "b107012ba800b08d", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n capabilities:\n python_execution:\n enabled: true\n mode: \"control_writes\" # Options: disabled | control_writes | all_code\n memory:\n enabled: true\n\n# Execution limits and safety controls\nexecution_control:\n # Agent control - Runtime behavior configuration\n agent_control:\n orchestration_mode: plan_first # Options: plan_first | react\n task_extraction_bypass_enabled: false # Skip LLM task extraction\n capability_selection_bypass_enabled: false # Skip LLM capability selection\n\n # Execution safety limits and control flow\n limits:\n max_reclassifications: 1 # Maximum times a task can be reclassified\n max_planning_attempts: 2 # Maximum number of planning attempts\n max_step_retries: 0 # Not used in Claude Code mode\n max_execution_time_seconds: 300 # Default timeout\n max_concurrent_classifications: 5 # Maximum concurrent LLM classification requests\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# Python executor settings\npython_executor:\n max_generation_retries: 3\n max_execution_retries: 3\n execution_timeout_seconds: 600\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # disable_servers: [] # e.g. [\"accelpapers\"] to remove a server\n # disable_agents: [] # e.g. [\"literature-search\"] to remove an agent\n # extra_servers: {} # Add custom MCP servers\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"cborg\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "e546870af0bd17e1c2c557befc6d66a6747aa0d9": {"t": 1785768164.1560526, "n": 10523, "h": "aa3416d62dde482c", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n capabilities:\n python_execution:\n enabled: true\n mode: \"control_writes\" # Options: disabled | control_writes | all_code\n memory:\n enabled: true\n\n# Execution limits and safety controls\nexecution_control:\n # Agent control - Runtime behavior configuration\n agent_control:\n orchestration_mode: plan_first # Options: plan_first | react\n task_extraction_bypass_enabled: false # Skip LLM task extraction\n capability_selection_bypass_enabled: false # Skip LLM capability selection\n\n # Execution safety limits and control flow\n limits:\n max_reclassifications: 1 # Maximum times a task can be reclassified\n max_planning_attempts: 2 # Maximum number of planning attempts\n max_step_retries: 0 # Not used in Claude Code mode\n max_execution_time_seconds: 300 # Default timeout\n max_concurrent_classifications: 5 # Maximum concurrent LLM classification requests\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# Python executor settings\npython_executor:\n max_generation_retries: 3\n max_execution_retries: 3\n execution_timeout_seconds: 600\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # disable_servers: [] # e.g. [\"accelpapers\"] to remove a server\n # disable_agents: [] # e.g. [\"literature-search\"] to remove an agent\n # extra_servers: {} # Add custom MCP servers\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"cborg\") }}\n #\n # Default model tier for Claude Code (optional):\n # default_model: opus # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "ffb8b888d617d0ce8bdb28c1f41ef8eede891771": {"t": 1785768167.390754, "n": 10589, "h": "bdac633f94f0226c", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\napproval:\n global_mode: \"selective\" # Options: disabled | selective | all_capabilities\n capabilities:\n python_execution:\n enabled: true\n mode: \"control_writes\" # Options: disabled | control_writes | all_code\n memory:\n enabled: true\n\n# Execution limits and safety controls\nexecution_control:\n # Agent control - Runtime behavior configuration\n agent_control:\n orchestration_mode: plan_first # Options: plan_first | react\n task_extraction_bypass_enabled: false # Skip LLM task extraction\n capability_selection_bypass_enabled: false # Skip LLM capability selection\n\n # Execution safety limits and control flow\n limits:\n max_reclassifications: 1 # Maximum times a task can be reclassified\n max_planning_attempts: 2 # Maximum number of planning attempts\n max_step_retries: 0 # Not used in Claude Code mode\n max_execution_time_seconds: 300 # Default timeout\n graph_recursion_limit: 100 # LangGraph recursion limit\n max_concurrent_classifications: 5 # Maximum concurrent LLM classification requests\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Timezone configuration for all containers and services\n timezone: ${TZ:-America/Los_Angeles}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n # Code generation configuration\n code_generator: \"{{ code_generator | default('basic') }}\" # Options: basic | claude_code\n generators:\n basic:\n # Reference the model config defined in 'models' section\n model_config_name: \"python_code_generator\"\n # Or define inline configuration here (overrides model_config_name):\n # provider: cborg\n # model_id: anthropic/claude-haiku\n # temperature: 0.7\n\n # Claude Code configuration (requires claude-agent-sdk)\n claude_code:\n claude_config_path: \"claude_generator_config.yml\" # Path to detailed config\n profile: \"fast\" # Options: fast | robust (or custom profiles you define)\n # Advanced settings are in claude_generator_config.yml\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n on_violation: \"skip\" # Options: error | skip\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Advanced: Extend for custom control system libraries (rarely needed)\n # patterns:\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# Python executor settings\npython_executor:\n max_generation_retries: 3\n max_execution_retries: 3\n execution_timeout_seconds: 600\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # disable_servers: [] # e.g. [\"accelpapers\"] to remove a server\n # disable_agents: [] # e.g. [\"literature-search\"] to remove an agent\n # extra_servers: {} # Add custom MCP servers\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider | default(\"cborg\") }}\n #\n # Default model tier for Claude Code (optional):\n # default_model: opus # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=America/Los_Angeles\n"}, "3b4f1841ec4952164d2164bb7a5efb5c6806d460": {"t": 1786067940.4225688, "n": 15921, "h": "854c6c264dde66ce", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services launched by `osprey deploy up`.\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n write_verification:\n enabled: false # Enable verification for production\n default_level: \"callback\" # Options: none | callback | readback\n default_tolerance_percent: 0.1\n timeout: 5.0\n fail_on_mismatch: false\n\n # Connector-specific configuration\n connector:\n mock:\n simulate_delays: false\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + cborg-style model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything. For a GENERIC OTLP backend\n # instead, set backend: generic and endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync (single source of truth).\n # `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env; the\n # ${VAR:-default} fallbacks keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category. (If you rebind the store off localhost, change the default\n # password first — these records include raw API bodies.)\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL CONFIGURATION\n# ============================================================\n# Browser-based Web Terminal UI. Independent of the CLI's `cli.theme` below\n# (console-only) — the two never share a value.\n\nweb:\n # Default web UI theme: a theme FAMILY (e.g. \"osprey\", \"high-contrast\") or\n # a concrete theme id (e.g. \"high-contrast-light\") to pin a specific mode.\n # A user's in-browser choice (switcher selection, ?theme=, localStorage)\n # always overrides this default. An unknown value falls back to \"osprey\".\n theme: osprey\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# API PROVIDERS\n# ============================================================\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: http://localhost:11434\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n# TZ=UTC\n"}, "f326a2b69e6c71eddce9823b24f93c5dc7271da7": {"t": 1786068511.27222, "n": 25828, "h": "bbf5be37ab88c036", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\n# Facility identity — one block, two keys:\n# name — display name woven into the agent prompts (CLAUDE.md, subagents)\n# and the web-terminal landing page. Falls back to the older\n# top-level `facility_name`, then to the project name.\n# prefix — short abbreviation the deployed web stack uses for container\n# names (`-nginx`, `-web-`); must be a\n# non-empty valid Docker name start ([a-zA-Z0-9]).\n# facility:\n# name: \"Example Research Facility\"\n# prefix: ex\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services launched by `osprey deploy up`.\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware for `execute`\n# only — under `selective` every other tool is treated as `always`), skip (no\n# approval).\n# Scope: applied by the osprey_approval PreToolUse hook, so these policies reach\n# only tools whose matcher runs that hook. Tools with no approval hook (the\n# health check, channel-finder queries, most workspace tools) are gated by the\n# rendered settings.json permissions instead.\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed for hook-wired tools not listed below\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Shipped OFF here (the readers default to false when the section is absent).\n# Turning it on drives three things, not just a debug print: one line per hook\n# call to stderr; an append-only /.claude/hooks/hook_debug.jsonl that\n# is never rotated or size-capped (prune it yourself on a long-lived\n# deployment); and the web terminal's Safety-panel hook-activity feed, which\n# reads that file and shows nothing new while this is false. The\n# OSPREY_HOOK_DEBUG environment variable forces it on regardless of this key,\n# and the Safety panel's own toggle writes this key back into config.yml —\n# creating the section even though the template ships none.\n# hooks:\n# debug: true\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Subdirectory names within the agent data directory above.\nfile_paths:\n api_calls_dir: api_calls\n registry_exports_dir: registry_exports\n\n# Raw provider request/response dumps, written into the api_calls_dir above.\n# Off unless `save_all` is true; a debugging aid, not the telemetry pipeline\n# (that is claude_code.telemetry). The files are unrotated and contain full\n# request bodies, so turn it off again when you are done.\n# development:\n# api_calls:\n# save_all: true # default false — nothing is written without this\n# latest_only: false # DEFAULT true: one `_latest.txt` per call\n# # site, overwritten each time. false keeps every\n# # call under a timestamped name instead.\n# include_stack_trace: true # default false — record the full call stack\n\n# osprey:panel-port artifact\n# WORKSPACE tab of `osprey web` — the artifact gallery, served by its own web\n# server. No section ships here, so it runs at its defaults: it is a universal\n# panel — launched at `osprey web` startup whatever panels are configured, and\n# on the first artifact save in headless runs — and it binds 127.0.0.1:8086.\n# Port resolution is the one shared function every launcher and URL builder\n# uses: OSPREY_ARTIFACT_SERVER_PORT wins (multi-user deployments\n# export it per user), then `port` below, then the built-in 8086. `host` has no\n# env override. Uncomment to move it, or set `auto_launch: false` to switch it\n# off — the tab is then reported unavailable rather than serving a dead iframe.\n# artifact_server:\n# host: \"127.0.0.1\"\n# port: 8086\n# auto_launch: true\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Control system type, write access, limits checking, and verification\n# Default: mock connector (no hardware required, safe for development)\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n type: \"mock\" # Options: mock | epics\n\n # Write Access Control - Master safety switch\n writes_enabled: false # Set true only for production hardware control\n # write_tools: # Additional tools blocked by writes kill switch\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: false # Enable for production safety\n database_path: null # Path to channel_limits.json\n allow_unlisted_channels: true\n\n # Write Verification Configuration\n # How the connector confirms a write took effect. Applies only when the caller does\n # not name a level: osprey.runtime.write_channel scripts and direct connector calls.\n # The channel_write MCP tool, multi-channel writes, and Bluesky device writes pass an\n # explicit level (\"callback\"); for those, only a per-channel \"verification\" entry in\n # channel_limits.json changes the level, and only on single-channel tool writes.\n # A failed verification is not a hard stop: the channel_write MCP tool reports it\n # (verification.verified: false), osprey.runtime.write_channel discards it (a readback\n # mismatch is logged at debug level only), and only write_channel_checked raises (scan\n # plans, which write through the Bluesky bridge's connector-backed devices). A write\n # the CA layer could not deliver at all - for example a PV that never connects -\n # sets success: false, which does raise from osprey.runtime.write_channel.\n write_verification:\n default_level: \"callback\" # Options: none | callback | readback (\"none\" = no verification)\n default_tolerance_percent: 0.1 # \"readback\" only; per-channel override in channel_limits.json\n\n # Connector-specific configuration\n connector:\n mock:\n response_delay_ms: 10 # Simulated per-operation response delay\n noise_level: 0.01 # Relative noise on generated values (0-1)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5064\n use_name_server: false # Set true for SSH tunnels\n write_access:\n address: your-gateway.example.com # Replace with your EPICS gateway address\n port: 5084\n use_name_server: false\n\n # Pattern Detection (Security Layer)\n # Purpose: Detect ALL control system operations for approval workflows\n # Security: Catches approved API AND circumvention attempts (direct library calls)\n # Framework Default: Detects osprey.runtime API + EPICS/Tango/LabVIEW direct calls\n # Custom patterns EXTEND framework defaults by default.\n # Set mode: override to replace framework patterns entirely.\n # patterns:\n # mode: extend # extend (default) | override\n # write: ['my_custom_cs_lib\\.write\\(']\n # read: ['my_custom_cs_lib\\.read\\(']\n\n # Runtime Connectors\n # Used by: Capabilities and osprey.runtime API\n # Purpose: Actual I/O operations with built-in safety features\n\n# ============================================================\n# ARCHIVER CONFIGURATION\n# ============================================================\n# Backs the archiver_read tool with historical channel data.\n\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance (add an `epics_archiver.url`)\n type: mock_archiver # Change to 'epics_archiver' for production\n # epics_archiver:\n # url: https://archiver.example.com # Replace with your archiver address\n # timeout: 60\n\n# ============================================================\n# CLAUDE CODE CONFIGURATION\n# ============================================================\n# Controls `osprey claude regen` behavior.\n# Agents are auto-managed based on config and are not listed here.\n\nclaude_code:\n # ── CLI Version Pin (issue #218) ──────────────────────────\n # Pin the Claude Code CLI version to insulate this project from upstream\n # breaking releases. When set, `osprey claude chat` and `osprey web` launch\n # via `npx -y @anthropic-ai/claude-code@` instead of bare `claude`.\n # First run downloads the package; subsequent runs hit the npx cache.\n # Verify with `osprey health` after setting.\n # cli_version: \"2.1.146\"\n #\n # ── Server / Agent Overrides ──────────────────────────────\n # Disable framework servers or add custom MCP servers:\n # servers:\n # accelpapers:\n # enabled: false\n # my-server:\n # command: \"python\"\n # args: [\"-m\", \"my_mcp\"]\n # env:\n # MY_KEY: \"${MY_KEY}\"\n # hooks:\n # pre_tool_use: [approval] # Human approval for all tools\n # permissions:\n # allow: [read_data, list_items]\n # ask: [write_data]\n #\n # Override framework agents or define custom agents:\n # agents:\n # literature-search:\n # enabled: false\n # my-custom-agent:\n # description: \"Searches the facility's custom database.\"\n # condition: my_feature # Only enabled when my_feature is truthy in config\n # server_dependency: my-server # Only enabled when my-server is active\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + the provider's own\n # model IDs from api.providers[name].models)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc — grpc requires an explicit `endpoint:` below; it is refused against the auto-derived openobserve endpoint (HTTP only)\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything. For a GENERIC OTLP backend\n # instead, set backend: generic and endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT}.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync (single source of truth).\n # `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env; the\n # ${VAR:-default} fallbacks keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category. (If you rebind the store off localhost, change the default\n # password first — these records include raw API bodies.)\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL CONFIGURATION\n# ============================================================\n# Two sections, both read, easy to confuse: `web_terminal:` is the terminal\n# PROCESS (bind address, shell, watcher), `web:` is the browser UI it renders\n# (theme, panel tabs). `web_terminal:` has never shipped in a template, so\n# every key below is at its default.\n# web_terminal:\n# host: \"127.0.0.1\" # Bind address. A deployment that DECLARES\n# # OSPREY_TERMINAL_BIND_HOST — the multi-user compose\n# # sets it on every per-user container so nginx is the\n# # only off-host path — overrides both this key and\n# # --host, so this cannot punch through that chokepoint.\n# port: 8087 # Likewise outranked by OSPREY_TERMINAL_WEB_PORT.\n# shell: /path/to/claude\n# # argv spawned in every PTY. Setting it REPLACES the\n# # launcher, so it also defeats the\n# # claude_code.cli_version pin above.\n# max_background_sessions: 5\n# watch_dir: ./_agent_data\n# # Directory the Workspace file watcher follows. Unset,\n# # it falls back to ./_agent_data relative to the\n# # launch directory — it does NOT read\n# # agent_data.base_dir, so if you moved that, set this\n# # to match.\n\n# Browser-based Web Terminal UI. Independent of the CLI's `cli.theme` below\n# (console-only) — the two never share a value.\n\nweb:\n # Default web UI theme. Two forms, and the difference matters:\n # - a theme FAMILY (\"main\", \"desy\", \"high-contrast\", \"retro\") sets the\n # palette and leaves light/dark to each viewer's OS preference;\n # - a concrete theme id (\"desy-light\", \"high-contrast-light\") additionally\n # PINS light or dark as this deployment's default.\n # A user's in-browser choice (display menu, ?theme=, localStorage) always\n # overrides this default. An unknown value warns and falls back to \"main\".\n # In a multi-user deployment, a roster entry's `theme:` (rendered as\n # OSPREY_WEB_THEME) overrides this per user.\n theme: main\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# ============================================================\n# API PROVIDERS\n# ============================================================\n# Each entry takes `api_key`, `base_url`, and a `models` tier map (haiku /\n# sonnet / opus -> the model IDs that provider serves).\n#\n# `base_url` is the endpoint the agent itself uses. For the built-in\n# claude_code.provider names (anthropic, cborg, als-apg) a value set here\n# overrides the URL the framework ships; omit it to keep the built-in one. A\n# trailing `/v1` is stripped for the agent (it appends `/v1/messages`) but kept\n# for the OpenAI translation proxy, so leave it on gateways that need it.\n#\n# A fourth key is read but shipped by no template:\n#\n# api.providers..default_model_id: \n#\n# It is consulted only when framework code calls the completion API with\n# neither a model config nor an explicit model_id; without it, such a call\n# fails with \"Model ID required\". It does not pick the agent's model (that is\n# claude_code.default_model) and it never overrides the tier map above.\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n models: # Gateway serves no Opus-class model; o3-mini is the strongest\n haiku: gpt-4o-mini\n sonnet: gpt-4o\n opus: o3-mini\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n models: # GPT-5 sizes mapped onto the standard tiers\n haiku: gpt-5-nano\n sonnet: gpt-5-mini\n opus: gpt-5\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n models: # Gemini 2.5 sizes mapped onto the standard tiers\n haiku: gemini-2.5-flash-lite\n sonnet: gemini-2.5-flash\n opus: gemini-2.5-pro\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n models: # Local models — `ollama pull` each one before use\n haiku: mistral:7b\n sonnet: gpt-oss:20b\n opus: gpt-oss:120b\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n models: # Argo spells its model IDs without punctuation\n haiku: claudehaiku45\n sonnet: claudesonnet45\n opus: claudeopus41\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: ${ALS_APG_BASE_URL:-https://llm.gianlucamartino.com/v1}\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n # Custom Anthropic-compatible proxy example:\n # my-proxy:\n # api_key: ${MY_PROXY_API_KEY} # Set ANTHROPIC_AUTH_TOKEN to this in your shell\n # base_url: https://your-proxy.example.com\n # api_protocol: anthropic # Keep this line for an Anthropic-native\n # # gateway. Only anthropic, cborg and\n # # als-apg are recognised natively by\n # # name; every other provider is assumed\n # # to speak OpenAI protocol and is routed\n # # through the translation proxy to\n # # /chat/completions. Copying this block\n # # without it silently adds that hop.\n # Then set claude_code.provider: my-proxy above.\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# PROJECT_ROOT={{ project_root | default(\"/path/to/project\") }}\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python\n"}}, "src/osprey/templates/apps/hello_world/config.yml.j2": {"905f38b2c3830bc74e6b6953b43eb8c383ae628f": {"t": 1785746861.3345587, "n": 10762, "h": "fdf0b3f48f29bba4", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"subprocess\"\n\n # Build provenance: the environment declaration this project was built from.\n # Nothing reads it at run time — agent Python runs in this project's own .venv\n # when it has one, resolved at run time, so no interpreter path is recorded.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost. This is the\n # only container this minimal template deploys, so `osprey deploy up` needs a\n # container runtime (Docker/podman) running.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context, so\n # `endpoint` is intentionally OMITTED. OpenObserve auth reads the root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync. `osprey deploy up` mints a\n # strong ZO_ROOT_USER_PASSWORD into .env; the ${VAR:-default} fallbacks keep\n # the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n"}, "7fd858b80c0c8f2ce161eee0ca48a19f82917b0c": {"t": 1785768108.9258027, "n": 15070, "h": "9a8fd84e0e06517e", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\n# Facility identity — one block, two keys:\n# name — display name woven into the agent prompts (CLAUDE.md, subagents)\n# and the web-terminal landing page. Falls back to the older\n# top-level `facility_name`, then to the project name.\n# prefix — short abbreviation the deployed web stack uses for container\n# names (`-nginx`, `-web-`); must be a\n# non-empty valid Docker name start ([a-zA-Z0-9]).\n# facility:\n# name: \"Example Research Facility\"\n# prefix: ex\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n models: # Gateway serves no Opus-class model; o3-mini is the strongest\n haiku: gpt-4o-mini\n sonnet: gpt-4o\n opus: o3-mini\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n models: # GPT-5 sizes mapped onto the standard tiers\n haiku: gpt-5-nano\n sonnet: gpt-5-mini\n opus: gpt-5\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n models: # Gemini 2.5 sizes mapped onto the standard tiers\n haiku: gemini-2.5-flash-lite\n sonnet: gemini-2.5-flash\n opus: gemini-2.5-pro\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n models: # Local models — `ollama pull` each one before use\n haiku: mistral:7b\n sonnet: gpt-oss:20b\n opus: gpt-oss:120b\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n models: # Argo spells its model IDs without punctuation\n haiku: claudehaiku45\n sonnet: claudesonnet45\n opus: claudeopus41\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure control_system.connector.epics with your gateway addresses\n# (and control_system.connector.epics.timeout if you need a non-default\n# timeout — connector settings are always nested under their connector type)\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Additional tools blocked by the writes kill switch. Framework servers'\n # write-gated tools are added automatically; list custom server tools here so\n # `writes_enabled: false` refuses them too (the same list also drives the\n # headless read-only disallow set).\n # write_tools:\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n# Archiver Configuration\n# Backs the archiver_read tool with historical channel data.\n# Options:\n# - mock_archiver: Development mode (generates synthetic data)\n# - epics_archiver: EPICS Archiver Appliance (add an `epics_archiver.url`)\narchiver:\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any hook-wired tool not listed here\n# requires approval, so reads need an explicit \"skip\" entry. The policies reach\n# only tools whose matcher runs the approval hook — a tool with no approval hook\n# (the health check, most workspace tools) is gated by the rendered\n# settings.json permissions instead, not by this block.\n# \"selective\" is content-aware for `execute` only; every other tool under\n# \"selective\" is treated as \"always\".\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed for hook-wired tools not listed below\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Shipped OFF here (the readers default to false when the section is absent).\n# Turning it on drives three things, not just a debug print: one line per hook\n# call to stderr; an append-only /.claude/hooks/hook_debug.jsonl that\n# is never rotated or size-capped (prune it yourself on a long-lived\n# deployment); and the web terminal's Safety-panel hook-activity feed, which\n# reads that file and shows nothing new while this is false. The\n# OSPREY_HOOK_DEBUG environment variable forces it on regardless of this key,\n# and the Safety panel's own toggle writes this key back into config.yml —\n# creating the section even though the template ships none.\n# hooks:\n# debug: true\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"subprocess\"\n\n # Build provenance: the environment declaration this project was built from.\n # Nothing reads it at run time — agent Python runs in this project's own .venv\n # when it has one, resolved at run time, so no interpreter path is recorded.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\n# Subdirectory names within the agent data directory (see `agent_data:` below).\nfile_paths:\n api_calls_dir: api_calls\n registry_exports_dir: registry_exports\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# osprey:panel-port artifact\n# WORKSPACE tab. Universal: under `osprey web` it is launched at startup\n# whether or not any panels are configured (headless runs instead start it on\n# the first artifact save), so `auto_launch: false` is the only thing that stops\n# it — and when it does, the tab is reported unavailable rather than serving a\n# dead iframe. Port resolution is the one shared function every launcher and\n# URL builder uses: OSPREY_ARTIFACT_SERVER_PORT wins (multi-user deployments\n# export it per user), then `port` here, then the built-in 8086. `host` has no\n# env override.\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# Can also override via CONTAINER_RUNTIME environment variable\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost. This is the\n # only container this minimal template deploys, so `osprey deploy up` needs a\n # container runtime (Docker/podman) running.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc — grpc requires an explicit `endpoint:` below; it is refused against the auto-derived openobserve endpoint (HTTP only)\n # backend: openobserve auto-derives the endpoint per network context, so\n # `endpoint` is intentionally OMITTED. OpenObserve auth reads the root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync. `osprey deploy up` mints a\n # strong ZO_ROOT_USER_PASSWORD into .env; the ${VAR:-default} fallbacks keep\n # the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n"}, "14cd3817731d9423ff039640d872bf31555e400a": {"t": 1785768110.1933966, "n": 10762, "h": "fdf0b3f48f29bba4", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"subprocess\"\n\n # Build provenance: the environment declaration this project was built from.\n # Nothing reads it at run time — agent Python runs in this project's own .venv\n # when it has one, resolved at run time, so no interpreter path is recorded.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost. This is the\n # only container this minimal template deploys, so `osprey deploy up` needs a\n # container runtime (Docker/podman) running.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context, so\n # `endpoint` is intentionally OMITTED. OpenObserve auth reads the root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync. `osprey deploy up` mints a\n # strong ZO_ROOT_USER_PASSWORD into .env; the ${VAR:-default} fallbacks keep\n # the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n"}, "2cb5bd5cd3969b8efd700a4d1be0a80d7a5f0685": {"t": 1785768111.3806474, "n": 10810, "h": "e2bb2db278bb0c51", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"subprocess\"\n\n # Build provenance: the environment declaration this project was built from.\n # Nothing reads it at run time — agent Python runs in this project's own .venv\n # when it has one, resolved at run time, so no interpreter path is recorded.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost. This is the\n # only container this minimal template deploys, so `osprey deploy up` needs a\n # container runtime (Docker/podman) running.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context, so\n # `endpoint` is intentionally OMITTED. OpenObserve auth reads the root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync. `osprey deploy up` mints a\n # strong ZO_ROOT_USER_PASSWORD into .env; the ${VAR:-default} fallbacks keep\n # the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n"}, "ecd4c20d41f93cb4a4d23ede5c468c3c78129934": {"t": 1785768112.807898, "n": 10390, "h": "8ff61653a14dee80", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost. This is the\n # only container this minimal template deploys, so `osprey deploy up` needs a\n # container runtime (Docker/podman) running.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context, so\n # `endpoint` is intentionally OMITTED. OpenObserve auth reads the root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync. `osprey deploy up` mints a\n # strong ZO_ROOT_USER_PASSWORD into .env; the ${VAR:-default} fallbacks keep\n # the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n"}, "95f7eb6363d1f7bab978b6affb80b1ced6e0bd45": {"t": 1785768114.196938, "n": 10002, "h": "0bd9440273e6f9e2", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost. This is the\n # only container this minimal template deploys, so `osprey deploy up` needs a\n # container runtime (Docker/podman) running.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context, so\n # `endpoint` is intentionally OMITTED. OpenObserve auth reads the root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync. `osprey deploy up` mints a\n # strong ZO_ROOT_USER_PASSWORD into .env; the ${VAR:-default} fallbacks keep\n # the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n"}, "986a688acd629d9a1d59859d9c9db1fa4854af7c": {"t": 1785768115.6865823, "n": 9751, "h": "5defef035dcef80e", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost. This is the\n # only container this minimal template deploys, so `osprey deploy up` needs a\n # container runtime (Docker/podman) running.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - openobserve\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context, so\n # `endpoint` is intentionally OMITTED. OpenObserve auth reads the root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync. `osprey deploy up` mints a\n # strong ZO_ROOT_USER_PASSWORD into .env; the ${VAR:-default} fallbacks keep\n # the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n"}, "755f6952bddc2afa80ded88d4a4b7614012ba432": {"t": 1785768116.8501759, "n": 6672, "h": "70ad96a4b27d3ccf", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "d8acf4baeade26211ddc5b71fc228b86f9881b73": {"t": 1785768118.1697457, "n": 6822, "h": "efb34bb5a1868ad1", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "b0cfc28b4f3f6ddc346b414c22fd9f10c828efcb": {"t": 1785768119.3729477, "n": 6229, "h": "7ab0b3d09a5a8e35", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "94ca3f490c9d5995809b79de10f7b8ad5b056c62": {"t": 1785768120.5387611, "n": 5960, "h": "cdd9b2ffeea0eef0", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "3725d191dfe4dbd8f79579bb052f634190b2f753": {"t": 1785768121.7143633, "n": 5801, "h": "5340a0528459c05f", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "a4960ef7d0624dd736da95fa48be862b2036813a": {"t": 1785768123.2002544, "n": 5798, "h": "ec5b7cd86d4884b1", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "951af1a5a17830285f3054f06983b2d5ebfb6339": {"t": 1785768124.9632998, "n": 4809, "h": "6b1413170f51a56e", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "870a3a0f2c57715ffe068049e8099b511e8f1281": {"t": 1785768126.2939818, "n": 4832, "h": "72d769d9b32542b8", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "b36ec08ef9c2673f0e3cefbe07e14ab08a611882": {"t": 1785768127.6898108, "n": 4850, "h": "986243905c904c44", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "79736eba301e32a622d6254e04ddac35f6169de2": {"t": 1785768128.9753902, "n": 4514, "h": "1b778c011e462c0e", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "3ee49fbab2a623030dd941a61313cb1015301d4a": {"t": 1785768130.225604, "n": 4744, "h": "65c4ec5afb31719d", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: anthropic/claude-haiku\n sonnet: anthropic/claude-sonnet\n opus: anthropic/claude-opus\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# KARMA — Claude Code Analytics Dashboard\n# ============================================================\n\nkarma:\n host: \"127.0.0.1\"\n port: 8741\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }}\n"}, "3b4f1841ec4952164d2164bb7a5efb5c6806d460": {"t": 1786067940.556416, "n": 10390, "h": "8ff61653a14dee80", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n# TZ=UTC\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure connector.epics with your gateway addresses\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Runtime connector settings\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any tool not listed here will\n# require approval, so reads need an explicit \"skip\" entry.\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"local\"\n python_env_path: {{ current_python_env }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# WORKSPACE & ARTIFACT GALLERY\n# ============================================================\n\nworkspace:\n base_dir: \"./osprey-workspace\"\n\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost. This is the\n # only container this minimal template deploys, so `osprey deploy up` needs a\n # container runtime (Docker/podman) running.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context, so\n # `endpoint` is intentionally OMITTED. OpenObserve auth reads the root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync. `osprey deploy up` mints a\n # strong ZO_ROOT_USER_PASSWORD into .env; the ${VAR:-default} fallbacks keep\n # the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n"}, "f326a2b69e6c71eddce9823b24f93c5dc7271da7": {"t": 1786068511.6568673, "n": 15091, "h": "9fb7d1692f486990", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Hello World — Minimal MCP + Mock Control System\n# One MCP server, zero complexity.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\n# Facility identity — one block, two keys:\n# name — display name woven into the agent prompts (CLAUDE.md, subagents)\n# and the web-terminal landing page. Falls back to the older\n# top-level `facility_name`, then to the project name.\n# prefix — short abbreviation the deployed web stack uses for container\n# names (`-nginx`, `-web-`); must be a\n# non-empty valid Docker name start ([a-zA-Z0-9]).\n# facility:\n# name: \"Example Research Facility\"\n# prefix: ex\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n models: # Gateway serves no Opus-class model; o3-mini is the strongest\n haiku: gpt-4o-mini\n sonnet: gpt-4o\n opus: o3-mini\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n models: # GPT-5 sizes mapped onto the standard tiers\n haiku: gpt-5-nano\n sonnet: gpt-5-mini\n opus: gpt-5\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n models: # Gemini 2.5 sizes mapped onto the standard tiers\n haiku: gemini-2.5-flash-lite\n sonnet: gemini-2.5-flash\n opus: gemini-2.5-pro\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n models: # Local models — `ollama pull` each one before use\n haiku: mistral:7b\n sonnet: gpt-oss:20b\n opus: gpt-oss:120b\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n models: # Argo spells its model IDs without punctuation\n haiku: claudehaiku45\n sonnet: claudesonnet45\n opus: claudeopus41\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: ${ALS_APG_BASE_URL:-https://llm.gianlucamartino.com/v1}\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# ============================================================\n# CONTROL SYSTEM CONFIGURATION\n# ============================================================\n# Mock mode: works instantly with any channel names (no hardware required)\n# Production mode: change type to 'epics' and configure the connector\n#\n# Migration to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Configure control_system.connector.epics with your gateway addresses\n# (and control_system.connector.epics.timeout if you need a non-default\n# timeout — connector settings are always nested under their connector type)\n# 3. Your code doesn't need to change!\n\ncontrol_system:\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch\n # When false, ALL writes are blocked (hard error, non-configurable)\n writes_enabled: false # Tutorial default: read-only\n # Set to true to explore the human approval flow\n\n # Additional tools blocked by the writes kill switch. Framework servers'\n # write-gated tools are added automatically; list custom server tools here so\n # `writes_enabled: false` refuses them too (the same list also drives the\n # headless read-only disallow set).\n # write_tools:\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n# Archiver Configuration\n# Backs the archiver_read tool with historical channel data.\n# Options:\n# - mock_archiver: Development mode (generates synthetic data)\n# - epics_archiver: EPICS Archiver Appliance (add an `epics_archiver.url`)\narchiver:\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n# ============================================================\n# HUMAN-IN-THE-LOOP APPROVAL\n# ============================================================\n# Per-tool approval policies enforced by the osprey_approval PreToolUse hook.\n# default_policy is fail-closed (\"always\"): any hook-wired tool not listed here\n# requires approval, so reads need an explicit \"skip\" entry. The policies reach\n# only tools whose matcher runs the approval hook — a tool with no approval hook\n# (the health check, most workspace tools) is gated by the rendered\n# settings.json permissions instead, not by this block.\n# \"selective\" is content-aware for `execute` only; every other tool under\n# \"selective\" is treated as \"always\".\n\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed for hook-wired tools not listed below\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Shipped OFF here (the readers default to false when the section is absent).\n# Turning it on drives three things, not just a debug print: one line per hook\n# call to stderr; an append-only /.claude/hooks/hook_debug.jsonl that\n# is never rotated or size-capped (prune it yourself on a long-lived\n# deployment); and the web terminal's Safety-panel hook-activity feed, which\n# reads that file and shows nothing new while this is false. The\n# OSPREY_HOOK_DEBUG environment variable forces it on regardless of this key,\n# and the Safety panel's own toggle writes this key back into config.yml —\n# creating the section even though the template ships none.\n# hooks:\n# debug: true\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\nexecution:\n execution_method: \"subprocess\"\n\n # Build provenance: the environment declaration this project was built from.\n # Nothing reads it at run time — agent Python runs in this project's own .venv\n # when it has one, resolved at run time, so no interpreter path is recorded.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Pinned to UTC for reproducibility — set your real facility zone in\n # production. Avoid ${TZ:-...}: inheriting the host $TZ makes archiver\n # queries and simulated events non-deterministic. Governs how the agent\n # interprets operator times and renders all timestamps.\n timezone: UTC\n\n# Subdirectory names within the agent data directory (see `agent_data:` below).\nfile_paths:\n api_calls_dir: api_calls\n registry_exports_dir: registry_exports\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# osprey:panel-port artifact\n# WORKSPACE tab. Universal: under `osprey web` it is launched at startup\n# whether or not any panels are configured (headless runs instead start it on\n# the first artifact save), so `auto_launch: false` is the only thing that stops\n# it — and when it does, the tab is reported unavailable rather than serving a\n# dead iframe. Port resolution is the one shared function every launcher and\n# URL builder uses: OSPREY_ARTIFACT_SERVER_PORT wins (multi-user deployments\n# export it per user), then `port` here, then the built-in 8086. `host` has no\n# env override.\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# Can also override via CONTAINER_RUNTIME environment variable\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Containerized companion services. Declare a service under `services:` (so\n# `osprey health` recognizes it) and add its name to `deployed_services:` to\n# actually launch it with `osprey deploy up`.\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n{% if deploy_services | default(true) %}\nservices:\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see the `telemetry:` block under `claude_code:` below\n # and docs/how-to/monitor-agent.rst). Deployed by default: the agent emits its\n # telemetry here, so `osprey deploy up` + `osprey web` gives you a working,\n # queryable observability store out of the box. Bound to localhost. This is the\n # only container this minimal template deploys, so `osprey deploy up` needs a\n # container runtime (Docker/podman) running.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop data older than N days (min 3). A\n # named volume has no size cap, so age is the size knob.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): connects to a services stack\n# another OSPREY project deployed on the same host, so it scaffolds nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Only the controls MCP server is enabled for this minimal template.\n\nclaude_code:\n servers:\n controls: {enabled: true}\n ariel: {enabled: false}\n channel-finder: {enabled: false}\n agents:\n logbook-search: {enabled: false}\n logbook-deep-research: {enabled: false}\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }}\n\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # The agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally\n # NOT wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc — grpc requires an explicit `endpoint:` below; it is refused against the auto-derived openobserve endpoint (HTTP only)\n # backend: openobserve auto-derives the endpoint per network context, so\n # `endpoint` is intentionally OMITTED. OpenObserve auth reads the root\n # credentials from the project .env — the SAME values the openobserve service\n # reads — so container and agent auth stay in sync. `osprey deploy up` mints a\n # strong ZO_ROOT_USER_PASSWORD into .env; the ${VAR:-default} fallbacks keep\n # the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. The store is local and loopback-bound, so\n # full capture is the default posture; set any to false to suppress that\n # category.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n"}}, "src/osprey/templates/apps/control_assistant/config.yml.j2": {"905f38b2c3830bc74e6b6953b43eb8c383ae628f": {"t": 1785746861.3546746, "n": 35898, "h": "e5393cc7257d0237", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file (see .env.example for the full provider list):\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# ALS_APG_API_KEY=your-als-apg-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\n{% if deploy_services | default(true) %}\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n # No password key: the Postgres password lives in the project .env as\n # ARIEL_DB_PASSWORD (minted by `osprey deploy up`), read by both the\n # container and the ariel DSN below — single source of truth, mirroring\n # the openobserve credentials convention.\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): this project connects to a\n# services stack another OSPREY project deployed on the same host, so it\n# scaffolds no services of its own. The empty `deployed_services` list is\n# explicit so `osprey deploy` finds it and launches nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA service running first — add\n # virtual_accelerator to deployed_services and run `osprey deploy up`\n # (see the \"Use the Virtual Accelerator\" how-to in the OSPREY docs).\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA service running first: add virtual_accelerator to\n # deployed_services and run `osprey deploy up` (see the \"Use the Virtual\n # Accelerator\" how-to in the OSPREY docs).\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n # Password reads ARIEL_DB_PASSWORD from the project .env — the SAME value\n # the postgresql compose service reads as POSTGRES_PASSWORD, so container\n # and agent auth stay in sync (single source of truth). `osprey deploy up`\n # mints a strong ARIEL_DB_PASSWORD into .env; the `${VAR:-ariel}` fallback\n # keeps the agent launchable before the first deploy. NOTE: an existing\n # Postgres data volume keeps the password it was initialized with — see\n # the postgresql compose template.\n uri: postgresql://ariel:${ARIEL_DB_PASSWORD:-ariel}@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n{%- if panel_presets is defined and panel_presets %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the web terminal \"+\" popover. The key is the menu label; the value is\n # the exact set of panels to show (non-member panels close). Members must be\n # enabled built-in or custom panel ids.\n presets:\n{%- for preset_name, preset_panels in panel_presets.items() %}\n {{ preset_name | tojson }}: [{{ preset_panels | join(\", \") }}]\n{%- endfor %}\n{%- else %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the \"+\" popover (non-member panels close). Uncomment to define some:\n # presets:\n # \"Machine setup\": [channel-finder, lattice, artifacts, okf]\n # \"Logbook review\": [ariel, artifacts]\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "7fd858b80c0c8f2ce161eee0ca48a19f82917b0c": {"t": 1785768108.5840056, "n": 54225, "h": "423be8480a8fd6c4", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\n# Facility identity — one block, two keys:\n# name — display name woven into the agent prompts (CLAUDE.md, subagents)\n# and the web-terminal landing page. Falls back to the older\n# top-level `facility_name`, then to the project name.\n# prefix — short abbreviation the deployed web stack uses for container\n# names (`-nginx`, `-web-`); must be a\n# non-empty valid Docker name start ([a-zA-Z0-9]).\n# facility:\n# name: \"Example Research Facility\"\n# prefix: ex\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n#\n# Each entry takes `api_key`, `base_url`, and a `models` tier map (haiku /\n# sonnet / opus -> the model IDs that provider serves).\n#\n# `base_url` is the endpoint the agent itself uses. For the built-in\n# claude_code.provider names (anthropic, cborg, als-apg) a value set here\n# overrides the URL the framework ships; omit it to keep the built-in one. A\n# trailing `/v1` is stripped for the agent (it appends `/v1/messages`) but kept\n# for the OpenAI translation proxy, so leave it on gateways that need it.\n#\n# A fourth key is read but shipped by no template:\n#\n# api.providers..default_model_id: \n#\n# It is consulted only when framework code calls the completion API with\n# neither a model config nor an explicit model_id; without it, such a call\n# fails with \"Model ID required\". It does not pick the agent's model (that is\n# claude_code.default_model) and it never overrides the tier map above.\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n models: # Gateway serves no Opus-class model; o3-mini is the strongest\n haiku: gpt-4o-mini\n sonnet: gpt-4o\n opus: o3-mini\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n models: # GPT-5 sizes mapped onto the standard tiers\n haiku: gpt-5-nano\n sonnet: gpt-5-mini\n opus: gpt-5\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n models: # Gemini 2.5 sizes mapped onto the standard tiers\n haiku: gemini-2.5-flash-lite\n sonnet: gemini-2.5-flash\n opus: gemini-2.5-pro\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n models: # Local models — `ollama pull` each one before use\n haiku: mistral:7b\n sonnet: gpt-oss:20b\n opus: gpt-oss:120b\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n models: # Argo spells its model IDs without punctuation\n haiku: claudehaiku45\n sonnet: claudesonnet45\n opus: claudeopus41\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file (see .env.example for the full provider list):\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# ALS_APG_API_KEY=your-als-apg-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# PostgreSQL backs ARIEL logbook search; OpenObserve is the local telemetry\n# store. Declare a service here and add its name to `deployed_services:` to\n# launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n # No password key: the Postgres password lives in the project .env as\n # ARIEL_DB_PASSWORD (minted by `osprey deploy up`), read by both the\n # container and the agent's ARIEL DSN — which is derived from the keys in\n # this block rather than written out, so this is the single source of\n # truth for both. Mirrors the openobserve credentials convention.\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): this project connects to a\n# services stack another OSPREY project deployed on the same host, so it\n# scaffolds no services of its own. The empty `deployed_services` list is\n# explicit so `osprey deploy` finds it and launches nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware for `execute`\n# only — under `selective` every other tool is treated as `always`), skip (no\n# approval).\n# Scope: these policies are applied by the osprey_approval PreToolUse hook, so\n# they reach only tools whose matcher runs that hook. A tool with no approval\n# hook — the health check, channel-finder queries, most workspace tools — never\n# consults this block; what gates those is the rendered settings.json\n# permissions.\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed for hook-wired tools not listed below\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Shipped ON. This drives three things, not just a debug print:\n# 1. one line per hook call to stderr, visible in the terminal that launched\n# the agent;\n# 2. an append-only /.claude/hooks/hook_debug.jsonl — never rotated\n# and never size-capped, so prune it yourself on a long-lived deployment;\n# 3. the web terminal's Safety-panel hook-activity feed, which reads that\n# file and shows nothing new for as long as this is false (it keeps\n# displaying whatever was already logged).\n# The OSPREY_HOOK_DEBUG environment variable forces it on regardless of this key.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA service running first — add\n # virtual_accelerator to deployed_services and run `osprey deploy up`\n # (see the \"Use the Virtual Accelerator\" how-to in the OSPREY docs).\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Presets and personas pin this value when they are applied,\n # and this comment is preserved verbatim when they do — so\n # trust the value on this line, never the comment beside it.\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Additional tools blocked by the writes kill switch. Framework servers'\n # write-gated tools are added automatically; list custom server tools here so\n # `writes_enabled: false` refuses them too (the same list also drives the\n # headless read-only disallow set).\n # write_tools:\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # How the connector confirms a write took effect. The level below applies only when\n # the caller does not name one: osprey.runtime.write_channel scripts and direct\n # connector calls. The channel_write MCP tool, multi-channel writes, and Bluesky\n # device writes pass an explicit level (\"callback\"); for those, only a per-channel\n # \"verification\" entry in channel_limits.json changes the level, and only on\n # single-channel tool writes.\n # A failed verification is not a hard stop. The channel_write MCP tool reports it in\n # its result (verification.verified: false); osprey.runtime.write_channel discards it\n # (a readback mismatch is logged at debug level only); only write_channel_checked\n # raises (scan plans, which write through the Bluesky bridge's connector-backed\n # devices). A write the CA layer could not deliver at all - for example a PV that\n # never connects - is different: it sets success: false, which does raise from\n # osprey.runtime.write_channel and shows up as a failed write in the MCP tool.\n write_verification:\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification (off, on the paths above)\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n #\n # `mode` decides what your lists do to the framework set above:\n # extend (default) — your patterns are merged on top of the framework's.\n # override — the framework list is REPLACED by exactly what you\n # list here. That drops the circumvention detection\n # (epics.caput, tango.DeviceProxy, .put(), ...), so the\n # guarantee promised above no longer holds: direct\n # library calls in generated code stop being flagged for\n # approval. Only use it if your lists reproduce that\n # coverage themselves.\n #\n # Uncomment and add to customize:\n # patterns:\n # mode: extend # extend (default) | override\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA service running first: add virtual_accelerator to\n # deployed_services and run `osprey deploy up` (see the \"Use the Virtual\n # Accelerator\" how-to in the OSPREY docs).\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost — the one\n # host<->container CA configuration confirmed to work across container\n # runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n #\n # The gateway port is deliberately not written out: with it unset the\n # connector follows services.virtual_accelerator.port, so moving the\n # deployed soft-IOC's port is a one-place edit. Set `port:` on a gateway\n # only to reach a VA this project does not deploy — one on another host,\n # or a container published on a host port different from the one it\n # serves on.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n # port: 5074\n use_name_server: true\n write_access:\n address: localhost\n # port: 5074\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n # - mongodb_archiver: MongoDB time-series collection. Needs the\n # archiver-mongodb extra AND a mongodb_archiver block (see below) —\n # the connector has no defaults for the required keys.\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the control-system connector above, so live\n # reads and archiver data stay consistent. The machine file is derived from\n # `control_system.connector..simulation_file` rather than repeated\n # here. Switch scenarios with `osprey sim apply NAME...` (see the\n # sim-scenarios skill).\n #\n # Uncomment to override: an explicit `simulation_file` wins, and the\n # divergence from the control-system model is logged. Advanced users can\n # also set sample_rate_hz here. `noise_level` is accepted but applies only\n # to channels absent from the simulation machine model — every channel the\n # model serves carries its own per-channel noise, so under the shipped\n # machine.json it changes nothing.\n # mock_archiver:\n # simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n # MongoDB archiver — install the extra first:\n # pip install 'osprey-framework[archiver-mongodb]'\n # Every key below except port and timeout is required; the connector raises\n # on the first one missing. The password itself is never stored here — it is\n # read at connect time from the environment variable named by password_env.\n # mongodb_archiver:\n # host: mongodb.example.org\n # port: 27017 # Optional, defaults to 27017\n # name: my-archiver-database\n # collection: my-archiver-collection\n # auth: database-auth # Authentication database\n # username: my-username\n # password_env: MONGODB_READONLY_PASSWORD\n # timeout: 60 # Optional, defaults to 60 seconds\n\n# ============================================================\n# SCAN PLANS (BLUESKY BRIDGE)\n# ============================================================\n# `bluesky.plan_dirs` adds directories to the scan-plan catalog the Bluesky\n# bridge discovers plans from. Read this before using it — the trust it grants\n# is not obvious from the key name:\n#\n# Plans found through it load at the `preset` trust tier. The three\n# operator-supplied tiers — shipped, preset, facility — are exec'd on\n# discovery unconditionally: no validation gate, no review record. Only the\n# agent-authored `session` tier is hash-checked against a passing validation\n# record before its files are exec'd. A directory listed here is code you are\n# choosing to run, not data you are choosing to read.\n#\n# Trust order, ascending: shipped < preset < facility < session < unreviewed.\n# On a same-name collision a strictly higher tier overrides and a strictly\n# lower one is refused; equal tiers let the later-scanned file win.\n# BLUESKY_PLAN_DIRS (env, set per bridge instance) is the higher `facility`\n# tier, so it outranks this key.\n#\n# Paths are resolved by the process running the plan loader. The deployed\n# bridge is a container with this config.yml mounted at\n# /app/project/config.yml, so entries here must be IN-CONTAINER paths. To\n# publish a directory from the host instead, use the build profile's\n# `bluesky.plan_dir` — the build mounts it read-only and exports\n# BLUESKY_PLAN_DIRS for you (at the `facility` tier).\n# bluesky:\n# plan_dirs:\n# - /app/project/extra_plans\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey health\n# Your code doesn't need to change!\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\", \"hierarchical\", or \"middle_layer\"\n\n # osprey:panel-port channel_finder\n # CHANNELS tab: the channel-finder UI is a web server of its own, configured\n # under this section. It launches when `channel-finder` is in the panel list\n # under `web:` below AND this `channel_finder:` section exists — it does, so\n # 8092 is bound by default. Uncomment to move or disable it. (See the\n # COMPANION WEB SERVERS map further down for the resolution order.)\n # web:\n # host: \"127.0.0.1\"\n # port: 8092\n # auto_launch: true\n\n # Descriptive names for channels that belong to no device family, generated\n # offline by `osprey channel-finder build-database --use-llm`. That flag\n # hard-fails unless `provider` below is set — it has no fallback to\n # claude_code.provider or any other provider key — so uncomment this block\n # before using it. `model_id` is a tier (haiku | sonnet | opus) resolved\n # through api.providers[provider].models; a non-tier value is passed to the\n # provider unchanged. Build-time only: nothing reads it at run time.\n # channel_name_generation:\n # llm_model:\n # provider: {{ default_provider }}\n # model_id: haiku\n # max_tokens: 1000\n # llm_batch_size: 10\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # Preview rendering only — how `osprey channel-finder\n # preview` displays device families. The MCP server and the\n # web UI build their own prompt text and never consult it, so\n # changing it does not change what the model sees.\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n # duckdb_path gates the `query_channels` SQL/FTS tool: with it unset\n # that one tool returns `not_configured` and the other middle-layer\n # tools carry on working. Nothing in build, scaffold or deploy creates\n # the file — build it from the JSON above yourself:\n # python -m osprey.services.channel_finder.databases.duckdb_import \\\n # --json data/channel_databases/middle_layer.json \\\n # --output data/channel_databases/middle_layer.duckdb\n # A relative path resolves against this config file's directory.\n # duckdb_path: data/channel_databases/middle_layer.duckdb\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# Subdirectory names within the agent data directory (see `agent_data:` below).\nfile_paths:\n api_calls_dir: api_calls\n registry_exports_dir: registry_exports\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n # No `database.uri`: the DSN is derived from the `services.postgresql` block\n # above (username, database_name, port_host), so moving the database port or\n # renaming the database stays a one-place edit. Password reads\n # ARIEL_DB_PASSWORD from the project .env — the SAME value the postgresql\n # compose service reads as POSTGRES_PASSWORD, so container and agent auth\n # stay in sync (single source of truth). `osprey deploy up` mints a strong\n # ARIEL_DB_PASSWORD into .env; the fallback password `ariel` keeps the agent\n # launchable before the first deploy. NOTE: an existing Postgres data volume\n # keeps the password it was initialized with — see the postgresql compose\n # template.\n #\n # To point ARIEL at a database this project does not run (an existing\n # facility Postgres, say), set the DSN explicitly — an explicit uri wins over\n # the derived one:\n # database:\n # uri: postgresql://ariel:${ARIEL_DB_PASSWORD}@logbook-db.example.org:5432/ariel\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # osprey:panel-port ariel\n # ARIEL tab: the search UI is a web server of its own, configured under this\n # section rather than a top-level one. It launches when `ariel` is in the\n # panel list under `web:` below — this section existing is not required, so\n # 8085 is bound by default. Uncomment to move or disable it. (See the\n # COMPANION WEB SERVERS map further down for the resolution order.)\n # web:\n # host: \"127.0.0.1\"\n # port: 8085\n # auto_launch: true\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n # NOTE: entries seeded by `osprey sim apply` are keyword-only. Seeding skips\n # the enhancement passes AND drops the embedding tables outright, so\n # recovering semantic search takes two steps: `osprey ariel migrate` to\n # recreate the tables, then `osprey ariel enhance` to fill them. (`osprey\n # ariel sync` is not a shortcut here: it migrates, but then polls an\n # ingestion adapter this preset has none of, and fails before it enhances.)\n # Until then a semantic query disables itself at runtime and returns a notice\n # telling you to use keyword search — it does not silently return zero hits,\n # and it does not run the keyword search for you.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers; required (no default)\n model:\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default provider for EMBEDDING modules that name none of their own. It is\n # not a general fallback: the semantic_processor LLM module above requires\n # its own explicit `provider`.\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url).\n# Omit it and composition uses claude_code.provider; with neither set the\n# compose panel reports that logbook.composition.provider is unconfigured.\n# default_tier: which model tier to use when the operator doesn't choose one.\n# The operator can override the tier (haiku/sonnet/opus) from the UI; the tier\n# is mapped to a model ID via api.providers[provider].models — there is no\n# model_id key here. Add one only for a provider whose models mapping has no\n# entry for the tier you use; otherwise composition fails naming the gap.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n # osprey:panel-port okf\n # KNOWLEDGE tab. This section has two jobs: `bundle_path` above feeds the\n # facility-knowledge MCP server, `osprey knowledge`, and the OKF panel server\n # itself (the launcher passes the resolved path into the panel app), while\n # the three keys below configure that panel's web server. It launches when\n # `okf` is in the panel list under `web:` below and this section exists —\n # which it does, so this project binds 8093 by default. Uncomment to move or\n # disable it.\n # host: \"127.0.0.1\"\n # port: 8093\n # auto_launch: true\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# ============================================================\n# COMPANION WEB SERVERS (PANEL PORTS)\n# ============================================================\n# Every panel tab in `osprey web` is served by its own small web server, and\n# each one takes host/port/auto_launch from its own config section. These six\n# are the complete set — five of them have never appeared in a template\n# (artifact_server is the only one that ever shipped), so this is the map:\n#\n# tab keys live under default port port env var\n# WORKSPACE artifact_server 8086 OSPREY_ARTIFACT_SERVER_PORT\n# ARIEL ariel.web 8085 OSPREY_ARIEL_PORT\n# CHANNELS channel_finder.web 8092 OSPREY_CHANNEL_FINDER_PORT\n# LATTICE lattice_dashboard 8097 OSPREY_LATTICE_DASHBOARD_PORT\n# KNOWLEDGE facility_knowledge 8093 OSPREY_FACILITY_KNOWLEDGE_PORT\n# SYSTEM health.web 8094 OSPREY_HEALTH_PORT\n#\n# The stanza for each lives in the section named above; all six carry a\n# machine-readable marker comment so they can be found by name.\n#\n# Resolution order in every case: the env var above wins, then this config's\n# `port`, then the built-in default. Multi-user deployments export the env var\n# per user (the per-user containers share one network namespace), which is why\n# config alone is never the last word. `host` has no env override and defaults\n# to 127.0.0.1. The launcher resolves host/port through one shared function;\n# for WORKSPACE the web terminal uses that same function, but for the other\n# five it computes the panel's URL separately and the two are kept in step by\n# hand — so an env override must be set consistently.\n#\n# What actually starts a server: `auto_launch` (default true) AND — for every\n# panel except WORKSPACE — the panel being listed under `web:` at the bottom of\n# this file. CHANNELS, LATTICE and KNOWLEDGE additionally need their top-level\n# section to exist at all — with no section they never launch, whatever the\n# panel list says.\n\n# osprey:panel-port artifact\n# WORKSPACE tab. Universal: under `osprey web` it is launched at startup\n# regardless of the panel list (headless runs instead start it on the first\n# artifact save), so `auto_launch: false` is the only thing that stops it — and\n# when it does, the tab is reported unavailable rather than serving a dead\n# iframe.\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# osprey:panel-port lattice_dashboard\n# LATTICE tab. No `lattice_dashboard:` section ships here, so the dashboard\n# never auto-launches in this project. Uncomment to turn it on, and add\n# `lattice` to the panel list under `web:` below.\n# lattice_dashboard:\n# host: \"127.0.0.1\"\n# port: 8097\n# auto_launch: true\n\n# osprey:panel-port system_health\n# SYSTEM tab. Unlike LATTICE and KNOWLEDGE it needs no section of its own — the\n# health framework has usable defaults — so with `system-health` in the panel\n# list it binds 8094 without appearing anywhere in this file. Uncomment only to\n# move it or to switch it off.\n# health:\n# web:\n# host: \"127.0.0.1\"\n# port: 8094\n# auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # Only the three built-ins above are recognised as Anthropic-native by\n # name. Every other provider is assumed to speak OpenAI protocol and is\n # routed through the translation proxy to /chat/completions. If yours is\n # Anthropic-native, add `api_protocol: anthropic` to its api.providers\n # entry to skip that hop:\n # api.providers.my-proxy.api_protocol: anthropic\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc — grpc requires an explicit `endpoint:` below; it is refused against the auto-derived openobserve endpoint (HTTP only)\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL\n# ============================================================\n# The terminal process itself (`osprey web`) — a different section from `web:`\n# below, which configures the panel tabs it renders. Both are read; neither has\n# ever shipped in a template, so every key here is at its default.\n# web_terminal:\n# host: \"127.0.0.1\" # Bind address. A deployment that DECLARES\n# # OSPREY_TERMINAL_BIND_HOST — the multi-user compose\n# # sets it on every per-user container so nginx is the\n# # only off-host path — overrides both this key and\n# # --host, so this cannot punch through that chokepoint.\n# port: 8087 # Likewise outranked by OSPREY_TERMINAL_WEB_PORT.\n# shell: /path/to/claude\n# # argv spawned in every PTY. Setting it REPLACES the\n# # launcher, so it also defeats the\n# # claude_code.cli_version pin above.\n# max_background_sessions: 5\n# watch_dir: ./_agent_data\n# # Directory the Workspace file watcher follows. Unset,\n# # it falls back to ./_agent_data relative to the\n# # launch directory — it does NOT read\n# # agent_data.base_dir, so if you moved that, set this\n# # to match.\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls the web terminal UI (osprey web): its theme and which panel tabs\n# appear. The Workspace panel is always shown; the Activity log is a separate\n# page, not a tab. Domain-specific panels are listed here — remove or disable\n# as needed.\n\nweb:\n # Default web UI theme: a theme FAMILY (e.g. \"osprey\", \"high-contrast\") or a\n # concrete theme id (e.g. \"high-contrast-light\") to pin a specific mode. A\n # user's in-browser choice (switcher selection, ?theme=, localStorage) always\n # overrides this default; an unknown value falls back to \"osprey\", which is\n # also what applies while this stays commented. Independent of `cli.theme`\n # above (console-only) — the two never share a value.\n # theme: osprey\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n # web.panels.my-grafana.rewrite_json_paths: [\"/config.json\"]\n # Panels are served through the terminal's reverse proxy, which\n # rewrites root-absolute paths in HTML/JS/CSS but passes JSON through\n # untouched (rewriting arbitrary API payloads would corrupt data). Some\n # backends bootstrap their UI from a JSON config endpoint carrying an\n # API base path — proxied, that UI then calls the browser's origin and\n # silently breaks. List those endpoints' path suffixes here to opt them\n # into the same rewrite.\n #\n # (The dotted keys above are build-profile `config:` override syntax, which\n # is expanded into nested YAML at build time. Inside this file use the\n # nested form — config.yml is never flattened; a dotted key written here\n # is stored verbatim and read by nothing.)\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n # Runtime panel control by the agent (optional; OFF by default). These sit at\n # the `web:` level, NOT under `panels:`:\n # allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- if panel_presets is defined and panel_presets %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the web terminal \"+\" popover. The key is the menu label; the value is\n # the exact set of panels to show (non-member panels close). Members must be\n # enabled built-in or custom panel ids.\n presets:\n{%- for preset_name, preset_panels in panel_presets.items() %}\n {{ preset_name | tojson }}: [{{ preset_panels | join(\", \") }}]\n{%- endfor %}\n{%- else %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the \"+\" popover (non-member panels close). Uncomment to define some:\n # presets:\n # \"Machine setup\": [channel-finder, lattice, artifacts, okf]\n # \"Logbook review\": [ariel, artifacts]\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n\n# ──────────────────────────────────────────────────────────────────────\n# Multi-user web terminals (optional)\n# ──────────────────────────────────────────────────────────────────────\n# Set by the multi-user presets, not by hand — documented here so the values\n# `osprey build` appends land in a named section instead of a bare tail.\n#\n# facility.name / facility.prefix\n# Display name, and the container-name prefix for the web stack\n# (`-nginx`, `-web-`). The only rule enforced is\n# non-emptiness — an empty prefix renders `-nginx`, which Docker\n# rejects, so `osprey build` lints for it. Beyond that the value must be\n# a valid Docker name start ([a-zA-Z0-9]); the shipped presets follow a\n# short lowercase convention (`ca`, `mu`). Also documented at the top of\n# this file.\n# deploy.fqdn\n# Browser-reachable origin for the landing page. The render REFUSES to\n# build the landing URL without it once any user is configured.\n# modules.web_terminals\n# The per-user terminal stack: enabled, image_source, nginx_port, the\n# per-user port families, and the user list. Per-user containers share\n# the host network namespace, so each companion panel needs its own port\n# family; families left unset fall back to the registry defaults.\n#\n# See the deploy skill's facility-config reference and\n# docs/how-to/web-terminal/multi-user.rst for the full schema.\n"}, "14cd3817731d9423ff039640d872bf31555e400a": {"t": 1785768110.0765681, "n": 35898, "h": "e5393cc7257d0237", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file (see .env.example for the full provider list):\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# ALS_APG_API_KEY=your-als-apg-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\n{% if deploy_services | default(true) %}\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n # No password key: the Postgres password lives in the project .env as\n # ARIEL_DB_PASSWORD (minted by `osprey deploy up`), read by both the\n # container and the ariel DSN below — single source of truth, mirroring\n # the openobserve credentials convention.\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): this project connects to a\n# services stack another OSPREY project deployed on the same host, so it\n# scaffolds no services of its own. The empty `deployed_services` list is\n# explicit so `osprey deploy` finds it and launches nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA service running first — add\n # virtual_accelerator to deployed_services and run `osprey deploy up`\n # (see the \"Use the Virtual Accelerator\" how-to in the OSPREY docs).\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA service running first: add virtual_accelerator to\n # deployed_services and run `osprey deploy up` (see the \"Use the Virtual\n # Accelerator\" how-to in the OSPREY docs).\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n # Password reads ARIEL_DB_PASSWORD from the project .env — the SAME value\n # the postgresql compose service reads as POSTGRES_PASSWORD, so container\n # and agent auth stay in sync (single source of truth). `osprey deploy up`\n # mints a strong ARIEL_DB_PASSWORD into .env; the `${VAR:-ariel}` fallback\n # keeps the agent launchable before the first deploy. NOTE: an existing\n # Postgres data volume keeps the password it was initialized with — see\n # the postgresql compose template.\n uri: postgresql://ariel:${ARIEL_DB_PASSWORD:-ariel}@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n{%- if panel_presets is defined and panel_presets %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the web terminal \"+\" popover. The key is the menu label; the value is\n # the exact set of panels to show (non-member panels close). Members must be\n # enabled built-in or custom panel ids.\n presets:\n{%- for preset_name, preset_panels in panel_presets.items() %}\n {{ preset_name | tojson }}: [{{ preset_panels | join(\", \") }}]\n{%- endfor %}\n{%- else %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the \"+\" popover (non-member panels close). Uncomment to define some:\n # presets:\n # \"Machine setup\": [channel-finder, lattice, artifacts, okf]\n # \"Logbook review\": [ariel, artifacts]\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "2cb5bd5cd3969b8efd700a4d1be0a80d7a5f0685": {"t": 1785768111.494082, "n": 35946, "h": "65fecfd05477b301", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file (see .env.example for the full provider list):\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# ALS_APG_API_KEY=your-als-apg-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\n{% if deploy_services | default(true) %}\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n # No password key: the Postgres password lives in the project .env as\n # ARIEL_DB_PASSWORD (minted by `osprey deploy up`), read by both the\n # container and the ariel DSN below — single source of truth, mirroring\n # the openobserve credentials convention.\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): this project connects to a\n# services stack another OSPREY project deployed on the same host, so it\n# scaffolds no services of its own. The empty `deployed_services` list is\n# explicit so `osprey deploy` finds it and launches nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA service running first — add\n # virtual_accelerator to deployed_services and run `osprey deploy up`\n # (see the \"Use the Virtual Accelerator\" how-to in the OSPREY docs).\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA service running first: add virtual_accelerator to\n # deployed_services and run `osprey deploy up` (see the \"Use the Virtual\n # Accelerator\" how-to in the OSPREY docs).\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n # Password reads ARIEL_DB_PASSWORD from the project .env — the SAME value\n # the postgresql compose service reads as POSTGRES_PASSWORD, so container\n # and agent auth stay in sync (single source of truth). `osprey deploy up`\n # mints a strong ARIEL_DB_PASSWORD into .env; the `${VAR:-ariel}` fallback\n # keeps the agent launchable before the first deploy. NOTE: an existing\n # Postgres data volume keeps the password it was initialized with — see\n # the postgresql compose template.\n uri: postgresql://ariel:${ARIEL_DB_PASSWORD:-ariel}@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n{%- if panel_presets is defined and panel_presets %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the web terminal \"+\" popover. The key is the menu label; the value is\n # the exact set of panels to show (non-member panels close). Members must be\n # enabled built-in or custom panel ids.\n presets:\n{%- for preset_name, preset_panels in panel_presets.items() %}\n {{ preset_name | tojson }}: [{{ preset_panels | join(\", \") }}]\n{%- endfor %}\n{%- else %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the \"+\" popover (non-member panels close). Uncomment to define some:\n # presets:\n # \"Machine setup\": [channel-finder, lattice, artifacts, okf]\n # \"Logbook review\": [ariel, artifacts]\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "a816e5dafbb8c7ab06401866de8a4594beadf188": {"t": 1785768113.077843, "n": 35069, "h": "38d18da483362771", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file (see .env.example for the full provider list):\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# ALS_APG_API_KEY=your-als-apg-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\n{% if deploy_services | default(true) %}\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n # No password key: the Postgres password lives in the project .env as\n # ARIEL_DB_PASSWORD (minted by `osprey deploy up`), read by both the\n # container and the ariel DSN below — single source of truth, mirroring\n # the openobserve credentials convention.\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): this project connects to a\n# services stack another OSPREY project deployed on the same host, so it\n# scaffolds no services of its own. The empty `deployed_services` list is\n# explicit so `osprey deploy` finds it and launches nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA service running first — add\n # virtual_accelerator to deployed_services and run `osprey deploy up`\n # (see the \"Use the Virtual Accelerator\" how-to in the OSPREY docs).\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA service running first: add virtual_accelerator to\n # deployed_services and run `osprey deploy up` (see the \"Use the Virtual\n # Accelerator\" how-to in the OSPREY docs).\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n # Password reads ARIEL_DB_PASSWORD from the project .env — the SAME value\n # the postgresql compose service reads as POSTGRES_PASSWORD, so container\n # and agent auth stay in sync (single source of truth). `osprey deploy up`\n # mints a strong ARIEL_DB_PASSWORD into .env; the `${VAR:-ariel}` fallback\n # keeps the agent launchable before the first deploy. NOTE: an existing\n # Postgres data volume keeps the password it was initialized with — see\n # the postgresql compose template.\n uri: postgresql://ariel:${ARIEL_DB_PASSWORD:-ariel}@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n{%- if panel_presets is defined and panel_presets %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the web terminal \"+\" popover. The key is the menu label; the value is\n # the exact set of panels to show (non-member panels close). Members must be\n # enabled built-in or custom panel ids.\n presets:\n{%- for preset_name, preset_panels in panel_presets.items() %}\n {{ preset_name | tojson }}: [{{ preset_panels | join(\", \") }}]\n{%- endfor %}\n{%- else %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the \"+\" popover (non-member panels close). Uncomment to define some:\n # presets:\n # \"Machine setup\": [channel-finder, lattice, artifacts, okf]\n # \"Logbook review\": [ariel, artifacts]\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "95f7eb6363d1f7bab978b6affb80b1ced6e0bd45": {"t": 1785768114.599079, "n": 33658, "h": "31c2fb474dcef736", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\n{% if deploy_services | default(true) %}\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): this project connects to a\n# services stack another OSPREY project deployed on the same host, so it\n# scaffolds no services of its own. The empty `deployed_services` list is\n# explicit so `osprey deploy` finds it and launches nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA container running first — see\n # docker/virtual-accelerator/README.md for the run command.\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA container running first: see docker/virtual-accelerator/README.md\n # for the run command.\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n{%- if panel_presets is defined and panel_presets %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the web terminal \"+\" popover. The key is the menu label; the value is\n # the exact set of panels to show (non-member panels close). Members must be\n # enabled built-in or custom panel ids.\n presets:\n{%- for preset_name, preset_panels in panel_presets.items() %}\n {{ preset_name | tojson }}: [{{ preset_panels | join(\", \") }}]\n{%- endfor %}\n{%- else %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the \"+\" popover (non-member panels close). Uncomment to define some:\n # presets:\n # \"Machine setup\": [channel-finder, lattice, artifacts, okf]\n # \"Logbook review\": [ariel, artifacts]\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "12d42d6c811644538a8005c3288393fbc8a31fa3": {"t": 1785768115.944049, "n": 33278, "h": "2966d140de84dbd1", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA container running first — see\n # docker/virtual-accelerator/README.md for the run command.\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA container running first: see docker/virtual-accelerator/README.md\n # for the run command.\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n{%- if panel_presets is defined and panel_presets %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the web terminal \"+\" popover. The key is the menu label; the value is\n # the exact set of panels to show (non-member panels close). Members must be\n # enabled built-in or custom panel ids.\n presets:\n{%- for preset_name, preset_panels in panel_presets.items() %}\n {{ preset_name | tojson }}: [{{ preset_panels | join(\", \") }}]\n{%- endfor %}\n{%- else %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the \"+\" popover (non-member panels close). Uncomment to define some:\n # presets:\n # \"Machine setup\": [channel-finder, lattice, artifacts, okf]\n # \"Logbook review\": [ariel, artifacts]\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "986a688acd629d9a1d59859d9c9db1fa4854af7c": {"t": 1785768117.5320818, "n": 32484, "h": "3ad7c8ecab722236", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA container running first — see\n # docker/virtual-accelerator/README.md for the run command.\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA container running first: see docker/virtual-accelerator/README.md\n # for the run command.\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "f0d05cbcb07f58d5dc5d2694e73858f8b081f015": {"t": 1785768119.0059755, "n": 29067, "h": "a8e3f0e675d65a73", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA container running first — see\n # docker/virtual-accelerator/README.md for the run command.\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA container running first: see docker/virtual-accelerator/README.md\n # for the run command.\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "1f9d6a15dd50fd3bfb9bae92f42bcec525961b12": {"t": 1785768120.3559365, "n": 29077, "h": "75e4f67001cd993f", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA container running first — see\n # docker/virtual-accelerator/README.md for the run command.\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA container running first: see docker/virtual-accelerator/README.md\n # for the run command.\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\", \"tuning\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "3844b5473478e273811410bba2273ae6ac2d5338": {"t": 1785768121.7512267, "n": 29058, "h": "58ae8e271481102f", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA container running first — see\n # docker/virtual-accelerator/README.md for the run command.\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA container running first: see docker/virtual-accelerator/README.md\n # for the run command.\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # docker/virtual-accelerator/probe/README.md for the empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\", \"tuning\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "5d0eb3f49dc01e4e321c83a531f347b290039e5a": {"t": 1785768123.1207998, "n": 27430, "h": "a0a91cef098a8b6b", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\", \"tuning\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "aa0d1252f3f3539ae5bb177c136c5f6525abcb0a": {"t": 1785768124.4323263, "n": 27404, "h": "55cb53abebfc1727", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "755f6952bddc2afa80ded88d4a4b7614012ba432": {"t": 1785768125.9938805, "n": 26987, "h": "d91cc1c7f890fea3", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "d8acf4baeade26211ddc5b71fc228b86f9881b73": {"t": 1785768127.4679966, "n": 27321, "h": "bbb89cbea34719d7", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "b0cfc28b4f3f6ddc346b414c22fd9f10c828efcb": {"t": 1785768128.9028318, "n": 26728, "h": "d6b5660cf7214041", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "94ca3f490c9d5995809b79de10f7b8ad5b056c62": {"t": 1785768130.31716, "n": 26355, "h": "446ecb0167523b25", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "d6e222648839ca9682e420767f1b34da6a4c08c9": {"t": 1785768131.8978634, "n": 26196, "h": "4fe1550ec2ef48d5", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "2e280263ab11836782ece014fc130a53442ef495": {"t": 1785768133.3544962, "n": 25670, "h": "f1e1febd084cebb2", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "3725d191dfe4dbd8f79579bb052f634190b2f753": {"t": 1785768134.7202656, "n": 24927, "h": "95f17a0cc5111e6e", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "b9917e7f9048cebe6575c5035bacfd22b6acc1db": {"t": 1785768137.9684114, "n": 25667, "h": "7ee1c4c41b5f02b6", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "578b21f1b0e8c710bc76aaae1c5c2c8cadca8e50": {"t": 1785768143.7284293, "n": 25450, "h": "679d8d1631b9a47e", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "b5db58b81720be8a36963fe73b80027690bf4b45": {"t": 1785768147.739587, "n": 25459, "h": "cb8a4d5f76d2eee6", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios by writing a scenario name into\n # data/simulation/active_scenario (see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios by writing a\n # scenario name into data/simulation/active_scenario (see the sim-scenarios\n # skill). Advanced users can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "979f8e175b48c051f3ed18d3ba733e89c0a66141": {"t": 1785768151.0432873, "n": 24924, "h": "1f32d93166cf93e6", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "06d2cf72ad588afea4977a6daaec47a5f0a201d2": {"t": 1785768153.697428, "n": 24923, "h": "dc6a12dad988491a", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Prompt Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed prompt artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey prompts list` to see all available artifacts.\n# Run `osprey prompts claim ` to claim an artifact for editing.\n#\n# prompts:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "0f32ec4eb52ae8d33e00cdc7240d291b37571536": {"t": 1785768156.68137, "n": 25474, "h": "2a10740f41fa2fcb", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Pipelines (compose search modules into higher-level strategies)\n # retrieval_modules: which search modules each pipeline uses\n pipelines:\n rag:\n enabled: true\n retrieval_modules: [keyword, semantic] # Semantic auto-disabled if unavailable\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search and RAG context\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n # ReAct agent configuration\n # provider: references api.providers for credentials (api_key, base_url)\n reasoning:\n provider: cborg # References api.providers.cborg for credentials\n model_id: haiku\n max_iterations: 5\n temperature: 0.1\n total_timeout_seconds: 120\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Prompt Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed prompt artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey prompts list` to see all available artifacts.\n# Run `osprey prompts claim ` to claim an artifact for editing.\n#\n# prompts:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "392ec41ae2f52ba1c719ecb6f591607bf286b52a": {"t": 1785768159.5176506, "n": 25973, "h": "c9e2096ae6d5df99", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (BPM01XPosition, BPM02XPosition, ...)\n # \"template\" - use pattern notation (BPM{01-10}{XPosition,YPosition})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/in_context_benchmark.json\n # In-context pipeline benchmark using UCSB FEL channels (30 queries)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/hierarchical_benchmark.json\n # Hierarchical pipeline benchmark with multi-level channel structure (47 queries)\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/middle_layer_benchmark.json\n # Middle layer pipeline benchmark with functional hierarchy (35 queries, 174 channels)\n{% endif %}\n\n # Benchmarking configuration\n # The active dataset is automatically determined by the selected pipeline_mode\n # Each pipeline defines its own benchmark dataset in channel_finder.pipelines..benchmark\n # You can override the dataset via CLI: python scripts/run_benchmarks.py --dataset custom_dataset\n benchmark:\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Pipelines (compose search modules into higher-level strategies)\n # retrieval_modules: which search modules each pipeline uses\n pipelines:\n rag:\n enabled: true\n retrieval_modules: [keyword, semantic] # Semantic auto-disabled if unavailable\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search and RAG context\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n # ReAct agent configuration\n # provider: references api.providers for credentials (api_key, base_url)\n reasoning:\n provider: cborg # References api.providers.cborg for credentials\n model_id: haiku\n max_iterations: 5\n temperature: 0.1\n total_timeout_seconds: 120\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Prompt Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed prompt artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey prompts list` to see all available artifacts.\n# Run `osprey prompts claim ` to claim an artifact for editing.\n#\n# prompts:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "a89f3b7b6b75ef0da53b2932f64763055b4fe423": {"t": 1785768162.4583507, "n": 25790, "h": "42b4194f4e7f4957", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (BPM01XPosition, BPM02XPosition, ...)\n # \"template\" - use pattern notation (BPM{01-10}{XPosition,YPosition})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/in_context_benchmark.json\n # In-context pipeline benchmark using UCSB FEL channels (30 queries)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/hierarchical_benchmark.json\n # Hierarchical pipeline benchmark with multi-level channel structure (47 queries)\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/middle_layer_benchmark.json\n # Middle layer pipeline benchmark with functional hierarchy (35 queries, 174 channels)\n{% endif %}\n\n # Benchmarking configuration\n # The active dataset is automatically determined by the selected pipeline_mode\n # Each pipeline defines its own benchmark dataset in channel_finder.pipelines..benchmark\n # You can override the dataset via CLI: python scripts/run_benchmarks.py --dataset custom_dataset\n benchmark:\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Pipelines (compose search modules into higher-level strategies)\n # retrieval_modules: which search modules each pipeline uses\n pipelines:\n rag:\n enabled: true\n retrieval_modules: [keyword, semantic] # Semantic auto-disabled if unavailable\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search and RAG context\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n # ReAct agent configuration\n # provider: references api.providers for credentials (api_key, base_url)\n reasoning:\n provider: cborg # References api.providers.cborg for credentials\n model_id: haiku\n max_iterations: 5\n temperature: 0.1\n total_timeout_seconds: 120\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Prompt Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed prompt artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey prompts list` to see all available artifacts.\n# Run `osprey prompts claim ` to claim an artifact for editing.\n#\n# prompts:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "951af1a5a17830285f3054f06983b2d5ebfb6339": {"t": 1785768165.1937804, "n": 25892, "h": "ed543285f4bae325", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/tiers/tier{{ tier | default(1) }}/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (BPM01XPosition, BPM02XPosition, ...)\n # \"template\" - use pattern notation (BPM{01-10}{XPosition,YPosition})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/in_context_benchmark.json\n # In-context pipeline benchmark using UCSB FEL channels (30 queries)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/tiers/tier{{ tier | default(1) }}/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/hierarchical_benchmark.json\n # Hierarchical pipeline benchmark with multi-level channel structure (47 queries)\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/tiers/tier{{ tier | default(1) }}/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/middle_layer_benchmark.json\n # Middle layer pipeline benchmark with functional hierarchy (35 queries, 174 channels)\n{% endif %}\n\n # Benchmarking configuration\n # The active dataset is automatically determined by the selected pipeline_mode\n # Each pipeline defines its own benchmark dataset in channel_finder.pipelines..benchmark\n # You can override the dataset via CLI: python scripts/run_benchmarks.py --dataset custom_dataset\n benchmark:\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Pipelines (compose search modules into higher-level strategies)\n # retrieval_modules: which search modules each pipeline uses\n pipelines:\n rag:\n enabled: true\n retrieval_modules: [keyword, semantic] # Semantic auto-disabled if unavailable\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search and RAG context\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n # ReAct agent configuration\n # provider: references api.providers for credentials (api_key, base_url)\n reasoning:\n provider: cborg # References api.providers.cborg for credentials\n model_id: haiku\n max_iterations: 5\n temperature: 0.1\n total_timeout_seconds: 120\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Prompt Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed prompt artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey prompts list` to see all available artifacts.\n# Run `osprey prompts claim ` to claim an artifact for editing.\n#\n# prompts:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "66c47dc8cd61c43c5015bf655b1e9ab173f7fe46": {"t": 1785768167.9462168, "n": 25938, "h": "8b0875c4018a15d4", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/tiers/tier{{ tier | default(1) }}/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (BPM01XPosition, BPM02XPosition, ...)\n # \"template\" - use pattern notation (BPM{01-10}{XPosition,YPosition})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/in_context_benchmark.json\n # In-context pipeline benchmark using UCSB FEL channels (30 queries)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/tiers/tier{{ tier | default(1) }}/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/hierarchical_benchmark.json\n # Hierarchical pipeline benchmark with multi-level channel structure (47 queries)\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/tiers/tier{{ tier | default(1) }}/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/middle_layer_benchmark.json\n # Middle layer pipeline benchmark with functional hierarchy (35 queries, 174 channels)\n{% endif %}\n\n # Benchmarking configuration\n # The active dataset is automatically determined by the selected pipeline_mode\n # Each pipeline defines its own benchmark dataset in channel_finder.pipelines..benchmark\n # You can override the dataset via CLI: python scripts/run_benchmarks.py --dataset custom_dataset\n benchmark:\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Pipelines (compose search modules into higher-level strategies)\n # retrieval_modules: which search modules each pipeline uses\n pipelines:\n rag:\n enabled: true\n retrieval_modules: [keyword, semantic] # Semantic auto-disabled if unavailable\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search and RAG context\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n # ReAct agent configuration\n # provider: references api.providers for credentials (api_key, base_url)\n reasoning:\n provider: cborg # References api.providers.cborg for credentials\n model_id: haiku\n max_iterations: 5\n temperature: 0.1\n total_timeout_seconds: 120\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider | default(\"anthropic\") }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Prompt Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed prompt artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey prompts list` to see all available artifacts.\n# Run `osprey prompts claim ` to claim an artifact for editing.\n#\n# prompts:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "7531f5402f3db0ab796a2a4400bd02013fa764ca": {"t": 1785768170.8301055, "n": 25836, "h": "8876cae02d2977a8", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file:\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n password: ariel\n port_host: 5432\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options:\n # - mock: Development mode (accepts any channel names, no hardware)\n # - epics: Production EPICS control system\n type: mock # <-- Change to 'epics' for production\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n on_violation: \"skip\" # Options: \"error\" | \"skip\" (DEFAULT)\n # - error: Strict - ANY violation blocks entire operation\n # - skip: Resilient - skip invalid writes, continue with valid ones\n # Production: \"skip\" recommended for multi-channel operations\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector uses sensible defaults (no configuration needed)\n # Advanced users can configure: response_delay_ms, noise_level\n\n # EPICS connector (production mode)\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver uses sensible defaults (no configuration needed)\n # Advanced users can configure: sample_rate_hz, noise_level\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (BPM01XPosition, BPM02XPosition, ...)\n # \"template\" - use pattern notation (BPM{01-10}{XPosition,YPosition})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/in_context_benchmark.json\n # In-context pipeline benchmark using UCSB FEL channels (30 queries)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/hierarchical_benchmark.json\n # Hierarchical pipeline benchmark with multi-level channel structure (47 queries)\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n\n # Benchmark dataset for this pipeline\n benchmark:\n dataset_path: data/benchmarks/datasets/middle_layer_benchmark.json\n # Middle layer pipeline benchmark with functional hierarchy (35 queries, 174 channels)\n{% endif %}\n\n # Benchmarking configuration\n # The active dataset is automatically determined by the selected pipeline_mode\n # Each pipeline defines its own benchmark dataset in channel_finder.pipelines..benchmark\n # You can override the dataset via CLI: python scripts/run_benchmarks.py --dataset custom_dataset\n benchmark:\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n timezone: ${TZ:-UTC}\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n uri: postgresql://ariel:ariel@localhost:5432/ariel\n\n default_max_results: 10\n\n # Ingestion - demo logbook data (bundled with template)\n # For production: change adapter and source_url to your logbook system\n ingestion:\n adapter: generic_json\n source_url: data/logbook_seed/demo_logbook.json\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Pipelines (compose search modules into higher-level strategies)\n # retrieval_modules: which search modules each pipeline uses\n pipelines:\n rag:\n enabled: true\n retrieval_modules: [keyword, semantic] # Semantic auto-disabled if unavailable\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search and RAG context\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n # ReAct agent configuration\n # provider: references api.providers for credentials (api_key, base_url)\n reasoning:\n provider: cborg # References api.providers.cborg for credentials\n model_id: haiku\n max_iterations: 5\n temperature: 0.1\n total_timeout_seconds: 120\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider | default(\"anthropic\") }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider | default(\"anthropic\") }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = [\"ariel\", \"channel-finder\", \"tuning\"] %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Prompt Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed prompt artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey prompts list` to see all available artifacts.\n# Run `osprey prompts claim ` to claim an artifact for editing.\n#\n# prompts:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "3b4f1841ec4952164d2164bb7a5efb5c6806d460": {"t": 1786067940.58377, "n": 35069, "h": "38d18da483362771", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n host: localhost\n port: 11434\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: https://llm.gianlucamartino.com/v1\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file (see .env.example for the full provider list):\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# ALS_APG_API_KEY=your-als-apg-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n# TZ=UTC\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# Only PostgreSQL is needed for ARIEL logbook search\n\n{% if deploy_services | default(true) %}\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n # No password key: the Postgres password lives in the project .env as\n # ARIEL_DB_PASSWORD (minted by `osprey deploy up`), read by both the\n # container and the ariel DSN below — single source of truth, mirroring\n # the openobserve credentials convention.\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): this project connects to a\n# services stack another OSPREY project deployed on the same host, so it\n# scaffolds no services of its own. The empty `deployed_services` list is\n# explicit so `osprey deploy` finds it and launches nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware), skip (no approval)\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed: unknown tools require approval\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n channel_limits: skip # Read-only safety metadata, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Set debug: true to enable hook activity logging to stderr.\n# Visible in the terminal where Claude Code runs.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA service running first — add\n # virtual_accelerator to deployed_services and run `osprey deploy up`\n # (see the \"Use the Virtual Accelerator\" how-to in the OSPREY docs).\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Tutorial default: enabled for mock connector\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # Configures how channel writes are verified for accuracy\n write_verification:\n enabled: true # Enable verification by default\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n timeout: 5.0 # Timeout for verification operations\n fail_on_mismatch: false # false: log warning, true: raise error on verification failure\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n # Uncomment and add to customize:\n # patterns:\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n timeout: 5.0 # Default timeout in seconds\n\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA service running first: add virtual_accelerator to\n # deployed_services and run `osprey deploy up` (see the \"Use the Virtual\n # Accelerator\" how-to in the OSPREY docs).\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost:5064 — the\n # one host<->container CA configuration confirmed to work across\n # container runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n port: 5064\n use_name_server: true\n write_access:\n address: localhost\n port: 5064\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the mock control-system connector, so live\n # reads and archiver data stay consistent. Switch scenarios with\n # `osprey sim apply NAME...` (see the sim-scenarios skill). Advanced users\n # can also configure: sample_rate_hz, noise_level\n mock_archiver:\n simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey run\n# Your code doesn't need to change!\n\n# ============================================================\n# MACHINE STATE CONFIGURATION\n# ============================================================\nmachine_state:\n channels_file: \"data/machine_state_channels.json\"\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\" or \"hierarchical\"\n\n # Explicit channel address validation mode\n # Controls how explicit PV addresses in queries are validated against the channel finder database\n # Options:\n # - lenient (DEFAULT): Accept all explicit addresses, warn if not in database\n # Best for production - trusts user-provided addresses while providing feedback\n # Use case: Database has 200 curated PVs, but users may reference any of 5000+ actual PVs\n # - strict: Only accept channels that exist in database\n # Best for testing - ensures all channels are documented\n # - skip: No validation, accept all addresses immediately (fastest)\n # Best for performance - when you fully trust user inputs\n explicit_validation_mode: lenient\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # How to present device families to LLM:\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n processing:\n chunk_dictionary: false # Whether to chunk the database for large channel lists\n # false - send entire database in one LLM call (faster, recommended for <200 channels)\n # true - split database into chunks and search each independently\n chunk_size: 50 # Number of channels per chunk when chunk_dictionary=true\n max_correction_iterations: 2 # Maximum LLM correction attempts when invalid channels are found (0-3 recommended)\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Tree preview: compact database overview injected into LLM prompts\n # Helps the model reason about where channels live before navigating\n tree_preview:\n enabled: true\n max_depth: 3 # How deep to render the tree (0 = root only)\n max_children: 5 # Max children per node before truncating\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n # Execution settings\n execution:\n runs_per_query: 1 # Number of times to run each query\n delay_between_runs: 0 # Delay in seconds between runs\n continue_on_error: true # Continue even if some queries fail\n max_concurrent_queries: 5 # Maximum parallel queries\n query_selection: all # \"all\" or [0,1,2] or range notation\n\n # Output settings\n output:\n results_dir: data/benchmarks/results\n save_incremental: true # Save results after each query\n include_detailed_metrics: true # Include detailed timing/cost metrics\n\n # Evaluation thresholds\n evaluation:\n exact_match_weight: 1.0\n partial_match_weight: 0.5\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"local\" # Options: local | container\n python_env_path: {{ current_python_env }} # Path to Python with dependencies\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | vulcan | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# Note: Theme system logging uses the existing 'base' logger color\n# No additional logging configuration needed\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# File paths - Data storage organization\nfile_paths:\n agent_data_dir: _agent_data\n executed_python_scripts_dir: executed_scripts\n user_memory_dir: user_memory\n api_calls_dir: api_calls\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n database:\n # Password reads ARIEL_DB_PASSWORD from the project .env — the SAME value\n # the postgresql compose service reads as POSTGRES_PASSWORD, so container\n # and agent auth stay in sync (single source of truth). `osprey deploy up`\n # mints a strong ARIEL_DB_PASSWORD into .env; the `${VAR:-ariel}` fallback\n # keeps the agent launchable before the first deploy. NOTE: an existing\n # Postgres data volume keeps the password it was initialized with — see\n # the postgresql compose template.\n uri: postgresql://ariel:${ARIEL_DB_PASSWORD:-ariel}@localhost:5432/ariel\n\n default_max_results: 10\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers\n model:\n provider: {{ default_provider }}\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default embedding provider (fallback for modules without explicit provider)\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url)\n# default_tier: which model tier to use when operator doesn't choose one\n# The operator can override the tier (haiku/sonnet/opus) from the UI;\n# the tier is mapped to a model_id via api.providers[provider].models.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n model_id: {{ default_model | default(\"haiku\") }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# Artifact gallery web server (auto-launches on first artifact save)\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls which panel tabs appear in the web terminal (osprey web).\n# Universal panels (Workspace, Activity, Session Analytics) are always shown.\n# Domain-specific panels are listed here — remove or disable as needed.\n\nweb:\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n #\n # Runtime panel control by the agent (optional; OFF by default):\n # web.allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # web.runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n{%- if panel_presets is defined and panel_presets %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the web terminal \"+\" popover. The key is the menu label; the value is\n # the exact set of panels to show (non-member panels close). Members must be\n # enabled built-in or custom panel ids.\n presets:\n{%- for preset_name, preset_panels in panel_presets.items() %}\n {{ preset_name | tojson }}: [{{ preset_panels | join(\", \") }}]\n{%- endfor %}\n{%- else %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the \"+\" popover (non-member panels close). Uncomment to define some:\n # presets:\n # \"Machine setup\": [channel-finder, lattice, artifacts, okf]\n # \"Logbook review\": [ariel, artifacts]\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n"}, "f326a2b69e6c71eddce9823b24f93c5dc7271da7": {"t": 1786068511.5015879, "n": 54556, "h": "4a0bf340d15097aa", "c": "# ============================================================\n# {{ project_name }} Configuration\n# ============================================================\n# Accelerator Control System Assistant\n# Based on ALS Assistant deployment (arXiv:2509.17255)\n# Claude Code project — streamlined configuration.\n# ============================================================\n\n# Project identity\nproject_name: \"{{ project_name }}\"\n\n# Facility identity — one block, two keys:\n# name — display name woven into the agent prompts (CLAUDE.md, subagents)\n# and the web-terminal landing page. Falls back to the older\n# top-level `facility_name`, then to the project name.\n# prefix — short abbreviation the deployed web stack uses for container\n# names (`-nginx`, `-web-`); must be a\n# non-empty valid Docker name start ([a-zA-Z0-9]).\n# facility:\n# name: \"Example Research Facility\"\n# prefix: ex\n\nbuild_dir: ./build\n\n# Root directory (absolute path)\nproject_root: {{ project_root }}\n\n# ============================================================\n# API CONFIGURATION\n# ============================================================\n# API keys and endpoints for LLM providers\n#\n# Each entry takes `api_key`, `base_url`, and a `models` tier map (haiku /\n# sonnet / opus -> the model IDs that provider serves).\n#\n# `base_url` is the endpoint the agent itself uses. For the built-in\n# claude_code.provider names (anthropic, cborg, als-apg) a value set here\n# overrides the URL the framework ships; omit it to keep the built-in one. A\n# trailing `/v1` is stripped for the agent (it appends `/v1/messages`) but kept\n# for the OpenAI translation proxy, so leave it on gateways that need it.\n#\n# A fourth key is read but shipped by no template:\n#\n# api.providers..default_model_id: \n#\n# It is consulted only when framework code calls the completion API with\n# neither a model config nor an explicit model_id; without it, such a call\n# fails with \"Model ID required\". It does not pick the agent's model (that is\n# claude_code.default_model) and it never overrides the tier map above.\n\napi:\n providers:\n cborg:\n api_key: ${CBORG_API_KEY}\n base_url: https://api.cborg.lbl.gov/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku-4-5\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-7\n amsc-i2:\n api_key: ${AMSC_I2_API_KEY}\n base_url: https://api.i2-core.american-science-cloud.org/v1\n models: # Model IDs as named by this provider\n haiku: claude-haiku\n sonnet: claude-sonnet\n opus: claude-opus\n stanford:\n api_key: ${STANFORD_API_KEY}\n base_url: https://aiapi-prod.stanford.edu/v1\n models: # Gateway serves no Opus-class model; o3-mini is the strongest\n haiku: gpt-4o-mini\n sonnet: gpt-4o\n opus: o3-mini\n openai:\n api_key: ${OPENAI_API_KEY}\n base_url: https://api.openai.com/v1\n models: # GPT-5 sizes mapped onto the standard tiers\n haiku: gpt-5-nano\n sonnet: gpt-5-mini\n opus: gpt-5\n anthropic:\n api_key: ${ANTHROPIC_API_KEY}\n base_url: https://api.anthropic.com\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-5-20250929\n opus: claude-opus-4-6\n google:\n api_key: ${GOOGLE_API_KEY}\n base_url: https://generativelanguage.googleapis.com/v1beta\n models: # Gemini 2.5 sizes mapped onto the standard tiers\n haiku: gemini-2.5-flash-lite\n sonnet: gemini-2.5-flash\n opus: gemini-2.5-pro\n ollama:\n api_key: ollama\n base_url: ${OLLAMA_HOST:-http://localhost:11434}\n models: # Local models — `ollama pull` each one before use\n haiku: mistral:7b\n sonnet: gpt-oss:20b\n opus: gpt-oss:120b\n argo:\n api_key: ${ARGO_API_KEY}\n base_url: https://argo-bridge.cels.anl.gov\n models: # Argo spells its model IDs without punctuation\n haiku: claudehaiku45\n sonnet: claudesonnet45\n opus: claudeopus41\n als-apg:\n api_key: ${ALS_APG_API_KEY}\n base_url: ${ALS_APG_BASE_URL:-https://llm.gianlucamartino.com/v1}\n models:\n haiku: claude-haiku-4-5-20251001\n sonnet: claude-sonnet-4-6\n opus: claude-opus-4-6\n # DwarfStar (ds4) — local DeepSeek V4 server (https://github.com/antirez/ds4)\n # No API key required. If the server runs on a remote host, forward the port:\n # ssh -L 8000:127.0.0.1:8000 \n ds4:\n api_key: EMPTY # ds4 ignores auth; any non-empty string works\n base_url: http://127.0.0.1:8000/v1\n # ds4 serves two model sizes; mapped onto the standard tiers so ds4 is a\n # drop-in (flash = fast/cheap tier, pro = capable tiers).\n models:\n haiku: deepseek-v4-flash\n sonnet: deepseek-v4-pro\n opus: deepseek-v4-pro\n\n# ============================================================\n# ENVIRONMENT VARIABLES\n# ============================================================\n# Set these in your .env file (see .env.example for the full provider list):\n#\n# CBORG_API_KEY=your-cborg-key\n# AMSC_I2_API_KEY=your-amsc-key\n# STANFORD_API_KEY=...\n# ARGO_API_KEY=your-argo-key\n# ALS_APG_API_KEY=your-als-apg-key\n# OPENAI_API_KEY=sk-...\n# ANTHROPIC_API_KEY=sk-ant-...\n# GOOGLE_API_KEY=...\n#\n# Optional (for advanced use cases):\n# PROJECT_ROOT=/path/to/override # Override project_root from config\n# LOCAL_PYTHON_VENV=/path/to/venv/bin/python # Override Python environment\n\n# ============================================================\n# CONTAINER RUNTIME\n# ============================================================\n# Container runtime selection for deployment\n# Options: auto (default), docker, podman\n# - auto: Tries Docker first, then Podman\n# - docker: Use Docker Desktop (recommended for macOS/Windows)\n# - podman: Use Podman (recommended for Linux/security-focused)\n# Can also override via CONTAINER_RUNTIME environment variable\n\ncontainer_runtime: auto\n\n# ── Deployment ──────────────────────────────────────────────────────\n# Host interface the deployed services publish their ports on. The default\n# (127.0.0.1, applied when this block is absent) keeps every service\n# loopback-only — the safe state. Binding 0.0.0.0 exposes every deployed\n# service port to the network; review credentials first.\n# deployment:\n# bind_address: 127.0.0.1\n\n# ============================================================\n# SERVICE CONFIGURATION\n# ============================================================\n# PostgreSQL backs ARIEL logbook search; OpenObserve is the local telemetry\n# store. Declare a service here and add its name to `deployed_services:` to\n# launch it with `osprey deploy up`.\n\n{% if deploy_services | default(true) %}\nservices:\n postgresql:\n path: ./services/postgresql\n database_name: ariel\n username: ariel\n # No password key: the Postgres password lives in the project .env as\n # ARIEL_DB_PASSWORD (minted by `osprey deploy up`), read by both the\n # container and the agent's ARIEL DSN — which is derived from the keys in\n # this block rather than written out, so this is the single source of\n # truth for both. Mirrors the openobserve credentials convention.\n port_host: 5432\n\n # Local telemetry backend for the OSPREY agent — a single-binary OTLP\n # logs/metrics store (see docs/how-to/monitor-agent.rst). This reference\n # facility ships it LIVE: it is deployed below and the agent emits to it (see\n # the `telemetry:` block under `claude_code:`), so `osprey deploy up` +\n # `osprey web` gives you a working, production-shaped observability wiring to\n # copy. `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env.\n openobserve:\n path: ./services/openobserve\n port: 5080 # Host port for the OpenObserve UI + OTLP ingest\n retention_days: 14 # Growth bound: drop telemetry older than N days. A named\n # volume has no hard size cap, so age-based retention is\n # the \"size\" knob (min 3; OpenObserve default is ~10y).\n # `osprey health` reports this and warns if the backing\n # disk fills.\n\n# Services to deploy with `osprey deploy up`\ndeployed_services:\n - postgresql\n - openobserve\n{% else %}\n# Attached project (deploy_services: false): this project connects to a\n# services stack another OSPREY project deployed on the same host, so it\n# scaffolds no services of its own. The empty `deployed_services` list is\n# explicit so `osprey deploy` finds it and launches nothing.\nservices: {}\n\ndeployed_services: []\n{% endif %}\n\n# ============================================================\n# SAFETY CONTROLS\n# ============================================================\n\n# Approval workflow for sensitive operations\n# Policies: always (prompt every time), selective (content-aware for `execute`\n# only — under `selective` every other tool is treated as `always`), skip (no\n# approval).\n# Scope: these policies are applied by the osprey_approval PreToolUse hook, so\n# they reach only tools whose matcher runs that hook. A tool with no approval\n# hook — the health check, channel-finder queries, most workspace tools — never\n# consults this block; what gates those is the rendered settings.json\n# permissions.\napproval:\n enabled: true\n default_policy: \"always\" # Fail-closed for hook-wired tools not listed below\n tools:\n channel_write: always # Always prompt for hardware writes\n channel_read: skip # Read-only, no approval needed\n archiver_read: skip # Historical data, no approval needed\n execute: selective # Content-aware: checks write patterns + exec mode\n setup_patch: always # Config mutation needs approval\n entry_create: always # ARIEL logbook write\n\n# ── Hook observability ──────────────────────────────────────────────\n# Shipped ON. This drives three things, not just a debug print:\n# 1. one line per hook call to stderr, visible in the terminal that launched\n# the agent;\n# 2. an append-only /.claude/hooks/hook_debug.jsonl — never rotated\n# and never size-capped, so prune it yourself on a long-lived deployment;\n# 3. the web terminal's Safety-panel hook-activity feed, which reads that\n# file and shows nothing new for as long as this is false (it keeps\n# displaying whatever was already logged).\n# The OSPREY_HOOK_DEBUG environment variable forces it on regardless of this key.\nhooks:\n debug: true\n\n# ============================================================\n# CONTROL SYSTEM & ARCHIVER CONFIGURATION\n# Issue #18 - Control System Abstraction\n# ============================================================\n# Unified configuration for control systems and archivers.\n# Supports mock mode (development) and production mode (EPICS, etc.)\n#\n# Development Mode: Works instantly with any channel names (no hardware required)\n# Production Mode: Connects to real control system and archiver\n# Switch modes by changing 'type' field\n\ncontrol_system:\n # Control system type - determines which runtime connector to use\n # Note: Pattern detection is control-system-agnostic (same for all types)\n # Options (three-state switch):\n # - mock: Development mode (accepts any channel names, no hardware) — DEFAULT\n # - virtual_accelerator: Containerized PyAT-backed soft-IOC over real EPICS\n # Channel Access. Requires the VA service running first — add\n # virtual_accelerator to deployed_services and run `osprey deploy up`\n # (see the \"Use the Virtual Accelerator\" how-to in the OSPREY docs).\n # - epics: Production EPICS control system (ALS values below — untouched)\n type: mock # <-- Change to 'virtual_accelerator' or 'epics' for other modes\n\n # Write Access Control - Master safety switch for hardware control\n # This is OPERATOR-LEVEL authorization (always hard-stops if false)\n writes_enabled: true # Presets and personas pin this value when they are applied,\n # and this comment is preserved verbatim when they do — so\n # trust the value on this line, never the comment beside it.\n # For production: carefully review hardware implications before enabling\n # If disabled, ALL writes are blocked (non-configurable hard error)\n\n # Additional tools blocked by the writes kill switch. Framework servers'\n # write-gated tools are added automatically; list custom server tools here so\n # `writes_enabled: false` refuses them too (the same list also drives the\n # headless read-only disallow set).\n # write_tools:\n # - \"mcp__my_server__dangerous_write\"\n\n # Runtime Channel Limits Checking\n limits_checking:\n enabled: true\n database_path: \"data/channel_limits.json\"\n allow_unlisted_channels: true # Permissive mode for tutorial\n\n # Write Verification Configuration\n # How the connector confirms a write took effect. The level below applies only when\n # the caller does not name one: osprey.runtime.write_channel scripts and direct\n # connector calls. The channel_write MCP tool, multi-channel writes, and Bluesky\n # device writes pass an explicit level (\"callback\"); for those, only a per-channel\n # \"verification\" entry in channel_limits.json changes the level, and only on\n # single-channel tool writes.\n # A failed verification is not a hard stop. The channel_write MCP tool reports it in\n # its result (verification.verified: false); osprey.runtime.write_channel discards it\n # (a readback mismatch is logged at debug level only); only write_channel_checked\n # raises (scan plans, which write through the Bluesky bridge's connector-backed\n # devices). A write the CA layer could not deliver at all - for example a PV that\n # never connects - is different: it sets success: false, which does raise from\n # osprey.runtime.write_channel and shows up as a failed write in the MCP tool.\n write_verification:\n default_level: callback # Options: none, callback, readback\n # - none: Fast write, no verification (off, on the paths above)\n # - callback: Control system confirms processing (e.g., EPICS IOC callback)\n # - readback: Full verification with readback comparison\n default_tolerance_percent: 0.1 # Default tolerance: 0.1% (one per mil)\n # Only used for \"readback\" verification level\n # Can be overridden per-channel in channel_limits.json\n\n # Pattern Detection (Layer 1 - Security)\n # Purpose: Detect ALL control system operations in generated code for approval workflows\n #\n # SECURITY: Catches both approved API usage AND circumvention attempts\n # The framework automatically detects:\n # - Approved: osprey.runtime API (write_channel, read_channel) - has all safety features\n # - Security: Direct library calls (epics.caput, tango.DeviceProxy, etc.) - bypasses safety\n # - Future: Other control systems (LabVIEW, custom) - comprehensive detection\n #\n # This ensures LLM-generated code cannot bypass connector safety features (limits, verification)\n # by directly importing control system libraries.\n #\n # Advanced: Extend patterns for custom control system libraries (rarely needed)\n #\n # `mode` decides what your lists do to the framework set above:\n # extend (default) — your patterns are merged on top of the framework's.\n # override — the framework list is REPLACED by exactly what you\n # list here. That drops the circumvention detection\n # (epics.caput, tango.DeviceProxy, .put(), ...), so the\n # guarantee promised above no longer holds: direct\n # library calls in generated code stop being flagged for\n # approval. Only use it if your lists reproduce that\n # coverage themselves.\n #\n # Uncomment and add to customize:\n # patterns:\n # mode: extend # extend (default) | override\n # write:\n # - 'my_custom_cs_lib\\.write\\('\n # read:\n # - 'my_custom_cs_lib\\.read\\('\n\n # Runtime Connectors (Layer 2)\n # Used by: Capabilities (channel_read, channel_write, archiver_retrieval)\n # and osprey.runtime API (read_channel, write_channel)\n # Purpose: Actual I/O operations to control systems with built-in safety features\n connector:\n # Mock connector (development mode) — driven by the data-driven simulation\n # machine model below. Switch scenarios with `osprey sim apply NAME...`\n # (composes self-contained scenario bundles under data/simulation/scenarios/\n # and seeds their logbook entries; see the sim-scenarios skill).\n # Advanced users can also configure: response_delay_ms, noise_level\n mock:\n simulation_file: data/simulation/machine.json\n\n # Virtual Accelerator connector (middle rung of the three-state switch) —\n # a containerized PyAT-backed soft-IOC reached over real EPICS Channel\n # Access via VirtualAcceleratorConnector (an EPICSConnector subclass, so\n # the gateway shape below is identical to the `epics` block's). Requires\n # the VA service running first: add virtual_accelerator to\n # deployed_services and run `osprey deploy up` (see the \"Use the Virtual\n # Accelerator\" how-to in the OSPREY docs).\n #\n # simulation_file: same simulation machine model as the mock connector\n # above, so `osprey sim apply NAME...` stays type-aware and consistent\n # whether you're running mock or virtual_accelerator.\n #\n # Gateway config is CA name-server/TCP mode against localhost — the one\n # host<->container CA configuration confirmed to work across container\n # runtimes (broadcast discovery does not reliably cross the\n # macOS<->container VM boundary). See\n # src/osprey/services/virtual_accelerator/probe/README.md for the\n # empirical finding.\n #\n # The gateway port is deliberately not written out: with it unset the\n # connector follows services.virtual_accelerator.port, so moving the\n # deployed soft-IOC's port is a one-place edit. Set `port:` on a gateway\n # only to reach a VA this project does not deploy — one on another host,\n # or a container published on a host port different from the one it\n # serves on.\n virtual_accelerator:\n timeout: 5.0\n simulation_file: data/simulation/machine.json\n gateways:\n read_only:\n address: localhost\n # port: 5074\n use_name_server: true\n write_access:\n address: localhost\n # port: 5074\n use_name_server: true\n\n # EPICS connector (production mode) — untouched ALS production values.\n epics:\n timeout: 5.0\n gateways:\n read_only:\n address: cagw-alsdmz.als.lbl.gov\n port: 5064\n # use_name_server: Set to true for SSH tunnels (uses EPICS_CA_NAME_SERVERS)\n # Set to false for direct gateway (uses EPICS_CA_ADDR_LIST)\n # Defaults to false. Can be overridden via environment variable.\n use_name_server: false\n write_access:\n address: cagw-alsdmz.als.lbl.gov\n port: 5084\n use_name_server: false\n\n# Archiver Configuration\narchiver:\n # Archiver type determines which connector to use\n # Options:\n # - mock_archiver: Development mode (generates synthetic data)\n # - epics_archiver: EPICS Archiver Appliance\n # - mongodb_archiver: MongoDB time-series collection. Needs the\n # archiver-mongodb extra AND a mongodb_archiver block (see below) —\n # the connector has no defaults for the required keys.\n type: mock_archiver # <-- Change to 'epics_archiver' for production\n\n # Mock archiver (development mode) — synthesizes history from the same\n # simulation machine model as the control-system connector above, so live\n # reads and archiver data stay consistent. The machine file is derived from\n # `control_system.connector..simulation_file` rather than repeated\n # here. Switch scenarios with `osprey sim apply NAME...` (see the\n # sim-scenarios skill).\n #\n # Uncomment to override: an explicit `simulation_file` wins, and the\n # divergence from the control-system model is logged. Advanced users can\n # also set sample_rate_hz here. `noise_level` is accepted but applies only\n # to channels absent from the simulation machine model — every channel the\n # model serves carries its own per-channel noise, so under the shipped\n # machine.json it changes nothing.\n # mock_archiver:\n # simulation_file: data/simulation/machine.json\n\n # EPICS Archiver Appliance (production mode)\n epics_archiver:\n url: https://archiver.als.lbl.gov:8443\n timeout: 60 # Timeout for archiver requests in seconds\n\n # MongoDB archiver — install the extra first:\n # pip install 'osprey-framework[archiver-mongodb]'\n # Every key below except port and timeout is required; the connector raises\n # on the first one missing. The password itself is never stored here — it is\n # read at connect time from the environment variable named by password_env.\n # mongodb_archiver:\n # host: mongodb.example.org\n # port: 27017 # Optional, defaults to 27017\n # name: my-archiver-database\n # collection: my-archiver-collection\n # auth: database-auth # Authentication database\n # username: my-username\n # password_env: MONGODB_READONLY_PASSWORD\n # timeout: 60 # Optional, defaults to 60 seconds\n\n# ============================================================\n# SCAN PLANS (BLUESKY BRIDGE)\n# ============================================================\n# `bluesky.plan_dirs` adds directories to the scan-plan catalog the Bluesky\n# bridge discovers plans from. Read this before using it — the trust it grants\n# is not obvious from the key name:\n#\n# Plans found through it load at the `preset` trust tier. The three\n# operator-supplied tiers — shipped, preset, facility — are exec'd on\n# discovery unconditionally: no validation gate, no review record. Only the\n# agent-authored `session` tier is hash-checked against a passing validation\n# record before its files are exec'd. A directory listed here is code you are\n# choosing to run, not data you are choosing to read.\n#\n# Trust order, ascending: shipped < preset < facility < session < unreviewed.\n# On a same-name collision a strictly higher tier overrides and a strictly\n# lower one is refused; equal tiers let the later-scanned file win.\n# BLUESKY_PLAN_DIRS (env, set per bridge instance) is the higher `facility`\n# tier, so it outranks this key.\n#\n# Paths are resolved by the process running the plan loader. The deployed\n# bridge is a container with this config.yml mounted at\n# /app/project/config.yml, so entries here must be IN-CONTAINER paths. To\n# publish a directory from the host instead, use the build profile's\n# `bluesky.plan_dir` — the build mounts it read-only and exports\n# BLUESKY_PLAN_DIRS for you (at the `facility` tier).\n# bluesky:\n# plan_dirs:\n# - /app/project/extra_plans\n\n# Migration from tutorial to production:\n# 1. Change control_system.type from 'mock' to 'epics'\n# 2. Change archiver.type from 'mock_archiver' to 'epics_archiver'\n# 3. Test with: osprey health\n# Your code doesn't need to change!\n\n# ============================================================\n# CHANNEL FINDER CONFIGURATION\n# ============================================================\n# Natural language channel finding service configuration\n# Pipeline mode: {{ channel_finder_mode }}\n\nchannel_finder:\n # Active pipeline mode\n pipeline_mode: {{ default_pipeline }} # \"in_context\", \"hierarchical\", or \"middle_layer\"\n\n # osprey:panel-port channel_finder\n # CHANNELS tab: the channel-finder UI is a web server of its own, configured\n # under this section. It launches when `channel-finder` is in the panel list\n # under `web:` below AND this `channel_finder:` section exists — it does, so\n # 8092 is bound by default. Uncomment to move or disable it. (See the\n # COMPANION WEB SERVERS map further down for the resolution order.)\n # web:\n # host: \"127.0.0.1\"\n # port: 8092\n # auto_launch: true\n\n # Descriptive names for channels that belong to no device family, generated\n # offline by `osprey channel-finder build-database --use-llm`. That flag\n # hard-fails unless `provider` below is set — it has no fallback to\n # claude_code.provider or any other provider key — so uncomment this block\n # before using it. `model_id` is a tier (haiku | sonnet | opus) resolved\n # through api.providers[provider].models; a non-tier value is passed to the\n # provider unchanged. Build-time only: nothing reads it at run time.\n # channel_name_generation:\n # llm_model:\n # provider: {{ default_provider }}\n # model_id: haiku\n # max_tokens: 1000\n # llm_batch_size: 10\n\n # Pipeline-specific configurations\n pipelines:\n{% if enable_in_context %}\n # In-Context Pipeline (Semantic Search)\n # Best for: few hundred channels (example from UCSB FEL)\n in_context:\n database:\n type: template # Database type: \"template\" (with device family expansion) or \"flat\" (simple list)\n path: data/channel_databases/in_context.json\n presentation_mode: template # Preview rendering only — how `osprey channel-finder\n # preview` displays device families. The MCP server and the\n # web UI build their own prompt text and never consult it, so\n # changing it does not change what the model sees.\n # \"explicit\" - list all expanded channel names (StorageRing_BPM_01_Position_X, StorageRing_BPM_02_Position_X, ...)\n # \"template\" - use pattern notation (StorageRing_BPM_{01-10}_Position_{X,Y})\n\n # Inner LLM model for the query_channels subagent tool (set per benchmark cell at runtime)\n subagent_model: null\n{% endif %}\n{% if enable_hierarchical %}\n # Hierarchical Pipeline (Structured Navigation)\n # Best for: >1,000 channels with clean naming convention\n hierarchical:\n database:\n type: hierarchical # Always \"hierarchical\" for this pipeline mode\n path: data/channel_databases/hierarchical.json\n # Database must follow nested structure: system → family → device → field → subfield\n\n # Feedback loop: records successful navigation paths for identical future queries\n # Injects prior paths as hints so the LLM can resolve repeated queries faster\n feedback:\n enabled: true\n store_path: data/feedback/hierarchical_feedback.json\n{% endif %}\n{% if enable_middle_layer %}\n # Middle Layer Pipeline (React Agent with MML Database)\n # Best for: MATLAB Middle Layer (MML) style databases, functional hierarchy (System→Family→Field), device/sector filtering\n middle_layer:\n database:\n type: middle_layer # Always \"middle_layer\" for this pipeline mode\n path: data/channel_databases/middle_layer.json\n # Database must follow MML structure: System → Family → Field → ChannelNames\n # Supports optional _description fields and device/sector filtering via DeviceList\n # duckdb_path gates the `query_channels` SQL/FTS tool: with it unset\n # that one tool returns `not_configured` and the other middle-layer\n # tools carry on working. Nothing in build, scaffold or deploy creates\n # the file — build it from the JSON above yourself:\n # python -m osprey.services.channel_finder.databases.duckdb_import \\\n # --json data/channel_databases/middle_layer.json \\\n # --output data/channel_databases/middle_layer.duckdb\n # A relative path resolves against this config file's directory.\n # duckdb_path: data/channel_databases/middle_layer.duckdb\n{% endif %}\n\n # Benchmarking configuration\n # All paradigms share a single benchmark query set, tier-resolved at build\n # time per channel_finder_mode (in_context → tier 1, hierarchical/middle_layer\n # → tier 3; override with `osprey build --tier N`). Query file is\n # materialized to data/benchmarks/queries.json by the scaffolder.\n benchmark:\n # Single source of truth — all paradigms read the same query set.\n dataset_path: data/benchmarks/queries.json\n\n# ============================================================\n# EXECUTION INFRASTRUCTURE\n# ============================================================\n\n# Python execution configuration\nexecution:\n execution_method: \"subprocess\" # Agent Python runs as a host subprocess\n\n # The environment declaration this project was built from, recorded so the\n # build can be reproduced. Nothing reads it at run time, and editing it here\n # changes nothing — rebuild the project to change the environment.\n # python base interpreter the build profile declared. null means\n # the build used its own interpreter; its installed\n # packages are deliberately not carried over.\n # packages extra requirements installed on top of the base.\n # inherit_exclude distributions left out when a declared venv base is frozen.\n # No interpreter path is recorded: agent Python runs in this project's own\n # .venv when it has one, resolved at run time.\n environment:\n python: {{ environment_python | default(none) | tojson }}\n packages: {{ environment_packages | default([]) | tojson }}\n inherit_exclude: {{ environment_inherit_exclude | default([]) | tojson }}\n\n\n# ============================================================\n# CLI CONFIGURATION\n# ============================================================\n# Customize the command-line interface appearance\n\ncli:\n # Theme selection - Options: default | custom\n theme: \"default\"\n\n # Custom theme colors (only used when theme: custom)\n # Uncomment and modify to create your own color scheme\n # custom_theme:\n # primary: \"#C75F71\" # Main brand color\n # success: \"#9988A1\" # Success messages\n # accent: \"#F0B8B8\" # Interactive elements\n # command: \"#9988A1\" # Shell commands\n # path: \"#A2AE9D\" # File paths\n # info: \"#9988A1\" # Info messages\n\n # Custom ASCII banner (optional)\n # Uncomment to override the default Osprey banner\n # banner: |\n # Your custom ASCII art here\n\n\n# ============================================================\n# SYSTEM CONFIGURATION\n# ============================================================\n\nsystem:\n # Facility timezone: how the agent interprets operator-provided times\n # (parsed as facility-local) and renders all timestamps (with an explicit\n # offset). Pinned to UTC for reproducibility — set your real facility zone\n # (e.g. America/Los_Angeles) in production. Avoid ${TZ:-...}: inheriting the\n # host $TZ makes archiver queries and simulated events non-deterministic.\n timezone: UTC\n\n# Subdirectory names within the agent data directory (see `agent_data:` below).\nfile_paths:\n api_calls_dir: api_calls\n registry_exports_dir: registry_exports\n\n# ============================================================\n# ARIEL - Electronic Logbook Search\n# ============================================================\n# ARIEL provides semantic search over electronic logbooks.\n# Configure the logbook source and search/embedding options below.\n#\n# Provider credentials are defined in api.providers section above.\n# ARIEL references providers by name (e.g., \"ollama\", \"cborg\").\n\nariel:\n # No `database.uri`: the DSN is derived from the `services.postgresql` block\n # above (username, database_name, port_host), so moving the database port or\n # renaming the database stays a one-place edit. Password reads\n # ARIEL_DB_PASSWORD from the project .env — the SAME value the postgresql\n # compose service reads as POSTGRES_PASSWORD, so container and agent auth\n # stay in sync (single source of truth). `osprey deploy up` mints a strong\n # ARIEL_DB_PASSWORD into .env; the fallback password `ariel` keeps the agent\n # launchable before the first deploy. NOTE: an existing Postgres data volume\n # keeps the password it was initialized with — see the postgresql compose\n # template.\n #\n # To point ARIEL at a database this project does not run (an existing\n # facility Postgres, say), set the DSN explicitly — an explicit uri wins over\n # the derived one:\n # database:\n # uri: postgresql://ariel:${ARIEL_DB_PASSWORD}@logbook-db.example.org:5432/ariel\n\n # No `ingestion` block: this preset seeds the logbook from its simulation\n # scenario bundles via `osprey sim apply NAME...`, which composes the active\n # scenarios and seeds their logbook entries into Postgres (see the\n # sim-scenarios skill). For production, add an `ingestion` block pointing at\n # your logbook system (adapter + source_url) and use `osprey ariel ingest`.\n\n # osprey:panel-port ariel\n # ARIEL tab: the search UI is a web server of its own, configured under this\n # section rather than a top-level one. It launches when `ariel` is in the\n # panel list under `web:` below — this section existing is not required, so\n # 8085 is bound by default. Uncomment to move or disable it. (See the\n # COMPANION WEB SERVERS map further down for the resolution order.)\n # web:\n # host: \"127.0.0.1\"\n # port: 8085\n # auto_launch: true\n\n # Search modules (leaf-level search functions)\n # provider: references api.providers for credentials (api_key, base_url)\n # Both keyword and semantic are enabled by default.\n # If Ollama/pgvector are unavailable, ARIEL degrades gracefully to keyword-only.\n # NOTE: entries seeded by `osprey sim apply` are keyword-only. Seeding skips\n # the enhancement passes AND drops the embedding tables outright, so\n # recovering semantic search takes two steps: `osprey ariel migrate` to\n # recreate the tables, then `osprey ariel enhance` to fill them. (`osprey\n # ariel sync` is not a shortcut here: it migrates, but then polls an\n # ingestion adapter this preset has none of, and fails before it enhances.)\n # Until then a semantic query disables itself at runtime and returns a notice\n # telling you to use keyword search — it does not silently return zero hits,\n # and it does not run the keyword search for you.\n search_modules:\n keyword:\n enabled: true\n semantic:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n model: nomic-embed-text\n\n # Enhancement modules\n # provider: references api.providers for credentials\n enhancement_modules:\n # Semantic processor: LLM-based keyword extraction and summarization\n # Enables better keyword search quality\n semantic_processor:\n enabled: false # Requires LLM API calls. Enable for better search quality.\n provider: {{ default_provider }} # References api.providers; required (no default)\n model:\n model_id: {{ default_model }}\n max_tokens: 256\n\n # Text embedding: Vector embeddings for semantic search\n # Degrades gracefully if Ollama or pgvector is unavailable\n text_embedding:\n enabled: true # Degrades gracefully if Ollama/pgvector unavailable\n provider: ollama # References api.providers.ollama\n models:\n - name: nomic-embed-text\n dimension: 768\n\n # Default provider for EMBEDDING modules that name none of their own. It is\n # not a general fallback: the semantic_processor LLM module above requires\n # its own explicit `provider`.\n embedding:\n provider: ollama\n\n# ============================================================\n# LOGBOOK COMPOSITION\n# ============================================================\n# LLM settings for the logbook compose panel in the artifact gallery.\n# provider: references api.providers for credentials (api_key, base_url).\n# Omit it and composition uses claude_code.provider; with neither set the\n# compose panel reports that logbook.composition.provider is unconfigured.\n# default_tier: which model tier to use when the operator doesn't choose one.\n# The operator can override the tier (haiku/sonnet/opus) from the UI; the tier\n# is mapped to a model ID via api.providers[provider].models — there is no\n# model_id key here. Add one only for a provider whose models mapping has no\n# entry for the tier you use; otherwise composition fails naming the gap.\n\nlogbook:\n composition:\n provider: {{ default_provider }}\n default_tier: haiku # Options: haiku | sonnet | opus\n\n# ============================================================\n# FACILITY KNOWLEDGE\n# ============================================================\n# OKF bundle providing deep facility knowledge (subsystems, devices,\n# procedures, physics notes, references) to the facility_knowledge\n# MCP server. Resolved relative to this config file's directory\n# (project root). Replace with the absolute path to your own bundle\n# once you have customised the example content.\n\nfacility_knowledge:\n bundle_path: data/facility_knowledge\n\n # osprey:panel-port okf\n # KNOWLEDGE tab. This section has two jobs: `bundle_path` above feeds the\n # facility-knowledge MCP server, `osprey knowledge`, and the OKF panel server\n # itself (the launcher passes the resolved path into the panel app), while\n # the three keys below configure that panel's web server. It launches when\n # `okf` is in the panel list under `web:` below and this section exists —\n # which it does, so this project binds 8093 by default. Uncomment to move or\n # disable it.\n # host: \"127.0.0.1\"\n # port: 8093\n # auto_launch: true\n\n# ============================================================\n# AGENT DATA & ARTIFACT GALLERY\n# ============================================================\n# Working directory for artifacts, data context, screenshots, etc.\n# Resolved relative to this config file's directory (project root).\n\nagent_data:\n base_dir: \"./_agent_data\"\n\n# ============================================================\n# COMPANION WEB SERVERS (PANEL PORTS)\n# ============================================================\n# Every panel tab in `osprey web` is served by its own small web server, and\n# each one takes host/port/auto_launch from its own config section. These six\n# are the complete set — five of them have never appeared in a template\n# (artifact_server is the only one that ever shipped), so this is the map:\n#\n# tab keys live under default port port env var\n# WORKSPACE artifact_server 8086 OSPREY_ARTIFACT_SERVER_PORT\n# ARIEL ariel.web 8085 OSPREY_ARIEL_PORT\n# CHANNELS channel_finder.web 8092 OSPREY_CHANNEL_FINDER_PORT\n# LATTICE lattice_dashboard 8097 OSPREY_LATTICE_DASHBOARD_PORT\n# KNOWLEDGE facility_knowledge 8093 OSPREY_FACILITY_KNOWLEDGE_PORT\n# SYSTEM health.web 8094 OSPREY_HEALTH_PORT\n#\n# The stanza for each lives in the section named above; all six carry a\n# machine-readable marker comment so they can be found by name.\n#\n# Resolution order in every case: the env var above wins, then this config's\n# `port`, then the built-in default. Multi-user deployments export the env var\n# per user (the per-user containers share one network namespace), which is why\n# config alone is never the last word. `host` has no env override and defaults\n# to 127.0.0.1. The launcher resolves host/port through one shared function;\n# for WORKSPACE the web terminal uses that same function, but for the other\n# five it computes the panel's URL separately and the two are kept in step by\n# hand — so an env override must be set consistently.\n#\n# What actually starts a server: `auto_launch` (default true) AND — for every\n# panel except WORKSPACE — the panel being listed under `web:` at the bottom of\n# this file. CHANNELS, LATTICE and KNOWLEDGE additionally need their top-level\n# section to exist at all — with no section they never launch, whatever the\n# panel list says.\n\n# osprey:panel-port artifact\n# WORKSPACE tab. Universal: under `osprey web` it is launched at startup\n# regardless of the panel list (headless runs instead start it on the first\n# artifact save), so `auto_launch: false` is the only thing that stops it — and\n# when it does, the tab is reported unavailable rather than serving a dead\n# iframe.\nartifact_server:\n host: \"127.0.0.1\"\n port: 8086\n auto_launch: true\n\n# osprey:panel-port lattice_dashboard\n# LATTICE tab. No `lattice_dashboard:` section ships here, so the dashboard\n# never auto-launches in this project. Uncomment to turn it on, and add\n# `lattice` to the panel list under `web:` below.\n# lattice_dashboard:\n# host: \"127.0.0.1\"\n# port: 8097\n# auto_launch: true\n\n# osprey:panel-port system_health\n# SYSTEM tab. Unlike LATTICE and KNOWLEDGE it needs no section of its own — the\n# health framework has usable defaults — so with `system-health` in the panel\n# list it binds 8094 without appearing anywhere in this file. Uncomment only to\n# move it or to switch it off.\n# health:\n# web:\n# host: \"127.0.0.1\"\n# port: 8094\n# auto_launch: true\n\n# ============================================================\n# CLAUDE CODE INTEGRATION\n# ============================================================\n# Controls which MCP servers and agents are generated by `osprey claude regen`.\n#\n# Auto-derived servers (always present unless disabled):\n# controls (core)\n# python (core)\n# workspace (core)\n# ariel (core)\n#\n# Auto-derived from config sections (present when section exists):\n# channel_finder: section → channel-finder MCP server + channel-finder agent\n#\n# Auto-derived agents (always present unless disabled):\n# logbook-search, logbook-deep-research\n#\nclaude_code:\n # servers: # Override server settings\n # my-server: # Add a custom MCP server\n # command: \"node\"\n # args: [\"my-server.js\"]\n # env:\n # MY_VAR: \"value\"\n # agents: # Override agent settings\n # literature-search:\n # enabled: false # Disable an agent\n #\n # ── Model Provider ──────────────────────────────────────────\n # Which provider serves Claude models. Must support Anthropic Messages API.\n # Built-in: anthropic (direct), cborg (LBNL proxy), als-apg (ALS AWS proxy)\n # Custom proxy: any name defined under api.providers below\n # (must have base_url set; uses ANTHROPIC_AUTH_TOKEN + provider model IDs)\n # Only the three built-ins above are recognised as Anthropic-native by\n # name. Every other provider is assumed to speak OpenAI protocol and is\n # routed through the translation proxy to /chat/completions. If yours is\n # Anthropic-native, add `api_protocol: anthropic` to its api.providers\n # entry to skip that hop:\n # api.providers.my-proxy.api_protocol: anthropic\n # When set, `osprey claude regen` injects env vars into .claude/settings.json.\n # permissions: # Override Claude Code permission policies\n # allow: [] # Additional tools to auto-allow\n # deny: [] # Additional tools to deny\n # ask: [] # Additional tools requiring human approval\n # remove_deny: # Remove tools from default deny list\n # - Bash # Example: allow Bash for trusted facility\n provider: {{ default_provider }}\n default_model: {{ default_model | default(\"haiku\") }} # Options: haiku, sonnet, opus\n #\n # Override model IDs per tier (optional):\n # models:\n # haiku: anthropic/claude-haiku-alt\n #\n # Override model tier per agent (optional):\n # agent_models:\n # logbook-search: haiku\n # logbook-deep-research: sonnet\n #\n # ── Telemetry / Observability (on by default, local + loopback-bound) ──\n # Like every OSPREY template, this facility ships telemetry ON: `osprey deploy\n # up` + `osprey web` produces a working, production-shaped wiring where the\n # agent emits OTLP logs + metrics to the co-deployed OpenObserve store\n # (declared in `services:` and listed in `deployed_services:` above; UI at\n # http://localhost:5080). On by default: the underlying harness already records\n # every prompt, response, and API body to disk, so this adds no new exposure —\n # it just makes that data queryable in a local store you own. To turn it off,\n # set `enabled: false`. Tracing and OTEL_LOG_TOOL_CONTENT are intentionally NOT\n # wired. See docs/how-to/monitor-agent.rst.\n telemetry:\n enabled: true # Master switch (on for this reference facility)\n backend: openobserve # openobserve | generic\n protocol: http/protobuf # http/protobuf | grpc — grpc requires an explicit `endpoint:` below; it is refused against the auto-derived openobserve endpoint (HTTP only)\n # backend: openobserve auto-derives the endpoint per network context —\n # http://localhost:5080/api/ on the host, http://openobserve:5080/api/\n # inside the deploy network — so `endpoint` is intentionally OMITTED here.\n # (Hardcoding localhost would make the in-container dispatch worker emit to\n # its own loopback and silently drop everything.)\n #\n # OpenObserve auth + org routing. user/password read the OpenObserve root\n # credentials from the project .env — the SAME values the openobserve\n # service reads — so container and agent auth stay in sync (single source of\n # truth). `osprey deploy up` mints a strong ZO_ROOT_USER_PASSWORD into .env;\n # the ${VAR:-default} fallbacks (mirroring the openobserve compose defaults)\n # keep the agent launchable before the first deploy.\n openobserve:\n user: ${ZO_ROOT_USER_EMAIL:-root@example.com}\n password: ${ZO_ROOT_USER_PASSWORD:-Complexpass#123}\n org: default\n # Content gates — all default ON. This store captures full transcripts; it\n # is local and air-gapped, so full capture is the default posture. Set any\n # to false to suppress that category from emitted telemetry.\n log_user_prompts: true # Operator chat prompts\n log_assistant_responses: true # Agent replies\n log_tool_details: true # Tool names + arguments\n log_raw_api_bodies: true # Raw provider request/response bodies\n\n# ============================================================\n# WEB TERMINAL\n# ============================================================\n# The terminal process itself (`osprey web`) — a different section from `web:`\n# below, which configures the panel tabs it renders. Both are read; neither has\n# ever shipped in a template, so every key here is at its default.\n# web_terminal:\n# host: \"127.0.0.1\" # Bind address. A deployment that DECLARES\n# # OSPREY_TERMINAL_BIND_HOST — the multi-user compose\n# # sets it on every per-user container so nginx is the\n# # only off-host path — overrides both this key and\n# # --host, so this cannot punch through that chokepoint.\n# port: 8087 # Likewise outranked by OSPREY_TERMINAL_WEB_PORT.\n# shell: /path/to/claude\n# # argv spawned in every PTY. Setting it REPLACES the\n# # launcher, so it also defeats the\n# # claude_code.cli_version pin above.\n# max_background_sessions: 5\n# watch_dir: ./_agent_data\n# # Directory the Workspace file watcher follows. Unset,\n# # it falls back to ./_agent_data relative to the\n# # launch directory — it does NOT read\n# # agent_data.base_dir, so if you moved that, set this\n# # to match.\n\n# ============================================================\n# WEB PANEL CONFIGURATION\n# ============================================================\n# Controls the web terminal UI (osprey web): its theme and which panel tabs\n# appear. The Workspace panel is always shown; the Activity log is a separate\n# page, not a tab. Domain-specific panels are listed here — remove or disable\n# as needed.\n\nweb:\n # Default web UI theme. Two forms, and the difference matters:\n # - a theme FAMILY (\"main\", \"desy\", \"high-contrast\", \"retro\") sets the\n # palette and leaves light/dark to each viewer's OS preference;\n # - a concrete theme id (\"desy-light\", \"high-contrast-light\") additionally\n # PINS light or dark as this deployment's default.\n # A user's in-browser choice (display menu, ?theme=, localStorage) always\n # overrides this default; an unknown value warns and falls back to \"main\",\n # which is also what applies while this stays commented. In a multi-user\n # deployment, a roster entry's `theme:` (rendered as OSPREY_WEB_THEME)\n # overrides this per user. Independent of `cli.theme` above (console-only) —\n # the two never share a value.\n # theme: main\n{%- set _BUILTIN_PANELS = builtin_panels | default([\"ariel\", \"channel-finder\"]) %}\n{%- set _enabled_builtin_panels = selected_web_panels | select(\"in\", _BUILTIN_PANELS) | list %}\n{%- if default_panel is defined and default_panel %}\n # Tab focused on cold load. Falls back to \"artifacts\" (Workspace) when unset.\n default_panel: {{ default_panel }}\n{%- endif %}\n{%- if _enabled_builtin_panels %}\n panels:\n{%- for panel in _enabled_builtin_panels %}\n {{ panel }}:\n enabled: true\n{%- endfor %}\n # Custom panels are declared via `config:` dotted overrides, e.g.:\n # config:\n # web.panels.my-grafana.label: \"GRAFANA\"\n # web.panels.my-grafana.url: \"http://grafana.local:3000\"\n # web.panels.my-grafana.health_endpoint: \"/api/health\"\n # web.panels.my-grafana.path: \"/\"\n # web.panels.my-grafana.hidden: true # launch the panel but keep its tab hidden until the agent shows it\n # web.panels.my-grafana.rewrite_json_paths: [\"/config.json\"]\n # Panels are served through the terminal's reverse proxy, which\n # rewrites root-absolute paths in HTML/JS/CSS but passes JSON through\n # untouched (rewriting arbitrary API payloads would corrupt data). Some\n # backends bootstrap their UI from a JSON config endpoint carrying an\n # API base path — proxied, that UI then calls the browser's origin and\n # silently breaks. List those endpoints' path suffixes here to opt them\n # into the same rewrite.\n #\n # (The dotted keys above are build-profile `config:` override syntax, which\n # is expanded into nested YAML at build time. Inside this file use the\n # nested form — config.yml is never flattened; a dotted key written here\n # is stored verbatim and read by nothing.)\n{%- else %}\n panels: {} # No builtin panels enabled; custom panels (if any) added via config overrides\n{%- endif %}\n # Runtime panel control by the agent (optional; OFF by default). These sit at\n # the `web:` level, NOT under `panels:`:\n # allow_runtime_panels: true # let the agent register ad-hoc URL panels at runtime\n # runtime_panel_allowlist: [\"grafana.local:3000\"] # restrict registration targets to these host[:port]\n{%- if panel_presets is defined and panel_presets %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the web terminal \"+\" popover. The key is the menu label; the value is\n # the exact set of panels to show (non-member panels close). Members must be\n # enabled built-in or custom panel ids.\n presets:\n{%- for preset_name, preset_panels in panel_presets.items() %}\n {{ preset_name | tojson }}: [{{ preset_panels | join(\", \") }}]\n{%- endfor %}\n{%- else %}\n # Panel layouts (\"Layouts\"): named sets of panels a human applies in one click\n # from the \"+\" popover (non-member panels close). Uncomment to define some:\n # presets:\n # \"Machine setup\": [channel-finder, lattice, artifacts, okf]\n # \"Logbook review\": [ariel, artifacts]\n{%- endif %}\n\n# ──────────────────────────────────────────────────────────────────────\n# User-Owned Build Artifacts\n# ──────────────────────────────────────────────────────────────────────\n# Claim framework-managed build artifacts for in-place editing.\n# Regen skips user-owned files, preserving your customizations.\n# Run `osprey scaffold list` to see all available artifacts.\n# Run `osprey scaffold claim ` to claim an artifact for editing.\n#\n# scaffold:\n# user_owned:\n# - rules/facility # Auto-claimed on init — edit .claude/rules/facility.md\n\n# ──────────────────────────────────────────────────────────────────────\n# Multi-user web terminals (optional)\n# ──────────────────────────────────────────────────────────────────────\n# Set by the multi-user presets, not by hand — documented here so the values\n# `osprey build` appends land in a named section instead of a bare tail.\n#\n# facility.name / facility.prefix\n# Display name, and the container-name prefix for the web stack\n# (`-nginx`, `-web-`). The only rule enforced is\n# non-emptiness — an empty prefix renders `-nginx`, which Docker\n# rejects, so `osprey build` lints for it. Beyond that the value must be\n# a valid Docker name start ([a-zA-Z0-9]); the shipped presets follow a\n# short lowercase convention (`ca`, `mu`). Also documented at the top of\n# this file.\n# deploy.fqdn\n# Browser-reachable origin for the landing page. The render REFUSES to\n# build the landing URL without it once any user is configured.\n# modules.web_terminals\n# The per-user terminal stack: enabled, image_source, nginx_port, the\n# per-user port families, and the user list. Per-user containers share\n# the host network namespace, so each companion panel needs its own port\n# family; families left unset fall back to the registry defaults.\n#\n# See the deploy skill's facility-config reference and\n# docs/how-to/web-terminal/multi-user.rst for the full schema.\n"}}}}