2026-08-08 17:59:45 -03:00
#!/usr/bin/env python3
""" Keep Hermes agent provider catalogs and managed profiles current. """
from __future__ import annotations
import copy
2026-08-11 20:22:26 -03:00
import json
2026-08-08 17:59:45 -03:00
import os
import re
2026-08-10 23:28:35 -03:00
import shutil
import subprocess
2026-08-11 20:22:26 -03:00
import time
2026-08-08 17:59:45 -03:00
from dataclasses import dataclass
from pathlib import Path
from typing import Any , Iterable
import yaml
CODEX_BASELINE = " gpt-5.6-terra "
CLAUDE_BASELINE = " claude-opus-5 "
2026-08-12 23:08:21 -03:00
CLAUDE_SUBSCRIPTION_MODELS = (
" claude-haiku-4-5-20251001 " ,
" claude-fable-5 " ,
" claude-sonnet-5 " ,
" claude-opus-5 " ,
)
2026-08-08 17:59:45 -03:00
EFFORTS = ( " low " , " medium " , " high " , " xhigh " )
ATLAS_FALLBACK = {
" provider " : " custom " ,
2026-08-11 05:20:18 -03:00
" model " : " qwen2.5:14b-instruct-q4_0 " ,
2026-08-08 17:59:45 -03:00
" base_url " : " http://hermes-model-gate.hermes.svc.cluster.local:11434/v1 " ,
" api_key " : " ollama " ,
}
# Backwards-compatible name used by the focused unit tests and status tooling.
LOCAL_FALLBACK = ATLAS_FALLBACK
2026-08-11 20:22:26 -03:00
SWITCHYARD_PROVIDER = " atlas-switchyard "
SWITCHYARD_API = " http://hermes-switchyard.hermes.svc.cluster.local:9005/v1 "
SWITCHYARD_AUTO_ROUTE = " atlas/auto/maximum "
ROUTING_CATALOG_PATH = os . environ . get ( " HERMES_ROUTING_CATALOG_PATH " , " " ) . strip ( )
2026-08-08 17:59:45 -03:00
MANAGED_ENV_KEYS = {
2026-08-15 17:58:47 -03:00
" GIT_ASKPASS " ,
" GIT_TERMINAL_PROMPT " ,
}
RUNTIME_SECRET_ENV_KEYS = {
" ANTHROPIC_API_KEY " ,
" API_SERVER_KEY " ,
" CLAUDE_API_KEY " ,
2026-08-08 17:59:45 -03:00
" CLAUDE_CODE_OAUTH_TOKEN " ,
" GITEA_TOKEN " ,
" GITEA_USERNAME " ,
2026-08-15 17:58:47 -03:00
" HERMES_IMAGE_BROKER_KEY " ,
" OPENAI_API_KEY " ,
2026-08-08 17:59:45 -03:00
}
@dataclass ( frozen = True )
class Catalog :
""" Non-secret provider discovery result. """
provider : str
models : list [ str ]
live : bool
connected : bool
state : str
def _unique_models ( models : Iterable [ str ] ) - > list [ str ] :
""" Return normalized model IDs without changing provider order. """
seen : set [ str ] = set ( )
result : list [ str ] = [ ]
for model in models :
value = str ( model or " " ) . strip ( )
if value and value . lower ( ) not in seen :
result . append ( value )
seen . add ( value . lower ( ) )
return result
def model_version ( model : str ) - > tuple [ int , . . . ] :
""" Extract a sortable model version while ignoring dated aliases. """
value = re . sub ( r " - \ d {8} $ " , " " , model . lower ( ) )
groups = re . findall ( r " \ d+(?: \ . \ d+)* " , value )
parts : list [ int ] = [ ]
for group in groups :
parts . extend ( int ( piece ) for piece in group . split ( " . " ) )
return tuple ( parts ) or ( 0 , )
def _codex_quality ( model : str ) - > int :
""" Rank known Codex capability tiers for delegated implementation work. """
value = model . lower ( )
if " sol " in value :
return 60
if " terra " in value :
return 50
if " codex " in value and " spark " not in value :
return 45
if re . fullmatch ( r " gpt- \ d+(?: \ . \ d+)* " , value ) :
return 40
if " luna " in value :
return 20
if " mini " in value or " nano " in value :
return 10
if " spark " in value :
return 5
return 0
def choose_codex_model (
models : Iterable [ str ] , current : str = CODEX_BASELINE , * , balanced : bool = False
) - > str :
""" Choose a current full Codex model, retaining current when unknown. """
candidates = [ m for m in _unique_models ( models ) if m . lower ( ) . startswith ( " gpt- " ) ]
full = [ m for m in candidates if _codex_quality ( m ) > = 40 ]
pool = full or candidates
if not pool :
return current
newest = max ( model_version ( model ) for model in pool )
latest = [ model for model in pool if model_version ( model ) == newest ]
if balanced :
terra = [ model for model in latest if " terra " in model . lower ( ) ]
if terra :
return max ( terra , key = lambda model : ( _codex_quality ( model ) , model ) )
return max ( latest , key = lambda model : ( _codex_quality ( model ) , model ) )
def _choose_by_hints (
models : Iterable [ str ] , hints : tuple [ str , . . . ] , current : str , prefix : str
) - > str :
""" Pick the newest model in the first available capability class. """
candidates = [
model
for model in _unique_models ( models )
if model . lower ( ) . startswith ( prefix )
]
if not candidates :
return current
for hint in hints :
tier = [ model for model in candidates if hint in model . lower ( ) ]
if tier :
return max ( tier , key = lambda model : ( model_version ( model ) , model ) )
return max ( candidates , key = lambda model : ( model_version ( model ) , model ) )
def choose_codex_for_effort (
models : Iterable [ str ] , effort : str , current : str = CODEX_BASELINE
) - > str :
""" Choose the account-visible Codex tier for an allowed effort level. """
hints = {
" low " : ( " luna " , " mini " , " spark " , " terra " , " sol " , " codex " ) ,
" medium " : ( " terra " , " codex " , " sol " , " luna " , " mini " , " spark " ) ,
" high " : ( " sol " , " codex " , " terra " , " luna " , " mini " , " spark " ) ,
" xhigh " : ( " sol " , " codex " , " terra " , " luna " , " mini " , " spark " ) ,
}
if effort not in EFFORTS :
raise ValueError ( f " unsupported effort: { effort } " )
return _choose_by_hints ( models , hints [ effort ] , current , " gpt- " )
def _claude_quality ( model : str ) - > int :
""" Rank known Claude capability tiers for architecture and review work. """
value = model . lower ( )
if " opus " in value :
return 40
if " sonnet " in value :
return 30
if " fable " in value :
return 20
if " haiku " in value :
return 10
return 0
def choose_claude_model ( models : Iterable [ str ] , current : str = CLAUDE_BASELINE ) - > str :
""" Choose the newest full Claude reasoning model visible to the account. """
candidates = [ m for m in _unique_models ( models ) if m . lower ( ) . startswith ( " claude- " ) ]
full = [ m for m in candidates if _claude_quality ( m ) > = 30 ]
pool = full or candidates
if not pool :
return current
return max (
pool ,
key = lambda model : ( model_version ( model ) , _claude_quality ( model ) , model ) ,
)
def choose_claude_for_effort (
models : Iterable [ str ] , effort : str , current : str = CLAUDE_BASELINE
) - > str :
""" Choose the account-visible Claude tier for an allowed effort level. """
hints = {
" low " : ( " haiku " , " fable " , " sonnet " , " opus " ) ,
" medium " : ( " sonnet " , " fable " , " opus " , " haiku " ) ,
" high " : ( " opus " , " sonnet " , " fable " , " haiku " ) ,
" xhigh " : ( " opus " , " sonnet " , " fable " , " haiku " ) ,
}
if effort not in EFFORTS :
raise ValueError ( f " unsupported effort: { effort } " )
return _choose_by_hints ( models , hints [ effort ] , current , " claude- " )
def _read_yaml ( path : Path ) - > dict [ str , Any ] :
""" Read a mapping from YAML, returning an empty mapping when unavailable. """
if not path . is_file ( ) :
return { }
try :
value = yaml . safe_load ( path . read_text ( encoding = " utf-8 " ) )
except ( OSError , yaml . YAMLError ) :
return { }
return value if isinstance ( value , dict ) else { }
def _atomic_write ( path : Path , content : str , mode : int | None = None ) - > bool :
""" Replace a file only when its content changes. """
path . parent . mkdir ( parents = True , exist_ok = True )
try :
if path . read_text ( encoding = " utf-8 " ) == content :
if mode is not None :
path . chmod ( mode )
return False
except OSError :
pass
temporary = path . with_name ( f " . { path . name } . { os . getpid ( ) } .tmp " )
temporary . write_text ( content , encoding = " utf-8 " )
if mode is not None :
temporary . chmod ( mode )
os . replace ( temporary , path )
return True
def _write_yaml ( path : Path , value : dict [ str , Any ] , mode : int | None = None ) - > bool :
""" Serialize a mapping and atomically update the target YAML file. """
return _atomic_write ( path , yaml . safe_dump ( value , sort_keys = False ) , mode )
2026-08-11 20:22:26 -03:00
def _read_json ( path : Path ) - > dict [ str , Any ] :
""" Read a JSON mapping without treating a partial write as valid state. """
try :
value = json . loads ( path . read_text ( encoding = " utf-8 " ) )
except ( OSError , ValueError , json . JSONDecodeError ) :
return { }
return value if isinstance ( value , dict ) else { }
def _previous_provider_models (
previous : dict [ str , Any ] , provider : str
) - > tuple [ dict [ str , str ] , dict [ str , str ] , list [ str ] ] :
""" Return last-known-good effort, tier, and catalog values for a provider. """
providers = previous . get ( " providers " , { } )
record = providers . get ( provider , { } ) if isinstance ( providers , dict ) else { }
if not isinstance ( record , dict ) :
return { } , { } , [ ]
resolved = record . get ( " resolved " , { } )
tiers = record . get ( " tiers " , { } )
models = record . get ( " models " , [ ] )
return (
dict ( resolved ) if isinstance ( resolved , dict ) else { } ,
dict ( tiers ) if isinstance ( tiers , dict ) else { } ,
_unique_models ( models if isinstance ( models , list ) else [ ] ) ,
)
def build_routing_catalog (
codex : Catalog , claude : Catalog , previous : dict [ str , Any ] | None = None
) - > dict [ str , Any ] :
""" Build a current catalog while retaining working routes during outages. """
previous = previous or { }
providers : dict [ str , Any ] = { }
specifications = (
(
" codex " ,
codex ,
choose_codex_for_effort ,
{ " luna " : " low " , " terra " : " medium " , " sol " : " high " } ,
{
" low " : " gpt-5.6-luna " ,
" medium " : " gpt-5.6-terra " ,
" high " : " gpt-5.6-sol " ,
" xhigh " : " gpt-5.6-sol " ,
} ,
) ,
(
" claude " ,
claude ,
choose_claude_for_effort ,
2026-08-12 23:08:21 -03:00
{
" haiku " : " low " ,
" fable " : " medium " ,
" sonnet " : " high " ,
" opus " : " xhigh " ,
} ,
2026-08-11 20:22:26 -03:00
{
" low " : " claude-haiku-4-5-20251001 " ,
" medium " : " claude-sonnet-5 " ,
" high " : " claude-opus-5 " ,
" xhigh " : " claude-opus-5 " ,
} ,
) ,
)
for name , discovered , chooser , tier_efforts , defaults in specifications :
old_resolved , old_tiers , old_models = _previous_provider_models ( previous , name )
source_models = discovered . models if discovered . live else old_models
resolved : dict [ str , str ] = { }
for effort in EFFORTS :
current = str ( old_resolved . get ( effort ) or defaults [ effort ] )
resolved [ effort ] = (
chooser ( source_models , effort , current )
if source_models
else current
)
tiers : dict [ str , str ] = { }
for tier , effort in tier_efforts . items ( ) :
current = str ( old_tiers . get ( tier ) or defaults [ effort ] )
tier_matches = [ model for model in source_models if tier in model . lower ( ) ]
tiers [ tier ] = (
max ( tier_matches , key = lambda model : ( model_version ( model ) , model ) )
if tier_matches
else ( resolved [ effort ] if discovered . live else current )
)
providers [ name ] = {
" state " : discovered . state ,
" connected " : discovered . connected ,
" live " : discovered . live ,
" models " : _unique_models (
discovered . models
if discovered . live
else ( old_models or discovered . models )
) ,
" resolved " : resolved ,
" tiers " : tiers ,
}
return {
" schema_version " : 1 ,
" updated_at " : int ( time . time ( ) ) ,
" providers " : providers ,
}
def write_routing_catalog (
path : Path , codex : Catalog , claude : Catalog
) - > dict [ str , Any ] :
""" Atomically publish the catalog consumed by hosted and worker brokers. """
catalog = build_routing_catalog ( codex , claude , _read_json ( path ) )
_atomic_write ( path , json . dumps ( catalog , indent = 2 , sort_keys = True ) + " \n " , 0o644 )
return catalog
2026-08-08 17:59:45 -03:00
def _read_env ( path : Path ) - > dict [ str , str ] :
""" Read the small dotenv subset used by Hermes provider credentials. """
values : dict [ str , str ] = { }
try :
lines = path . read_text ( encoding = " utf-8 " ) . splitlines ( )
except OSError :
return values
for line in lines :
value = line . strip ( )
if not value or value . startswith ( " # " ) or " = " not in value :
continue
key , raw = value . removeprefix ( " export " ) . split ( " = " , 1 )
raw = raw . strip ( )
if len ( raw ) > = 2 and raw [ 0 ] == raw [ - 1 ] and raw [ 0 ] in " \" ' " :
raw = raw [ 1 : - 1 ]
values [ key . strip ( ) ] = raw
return values
def _update_profile_env ( path : Path , source : dict [ str , str ] ) - > None :
2026-08-15 17:58:47 -03:00
""" Refresh non-secret managed settings and remove stale credential copies. """
2026-08-08 17:59:45 -03:00
try :
old_lines = path . read_text ( encoding = " utf-8 " ) . splitlines ( )
except OSError :
old_lines = [ ]
kept = [
line
for line in old_lines
2026-08-15 17:58:47 -03:00
if not any (
line . lstrip ( ) . startswith ( f " { key } = " )
for key in MANAGED_ENV_KEYS | RUNTIME_SECRET_ENV_KEYS
)
2026-08-08 17:59:45 -03:00
]
kept . extend (
f " { key } = { source [ key ] } " for key in sorted ( MANAGED_ENV_KEYS ) if source . get ( key )
)
_atomic_write ( path , " \n " . join ( kept ) . rstrip ( ) + " \n " , 0o600 )
def _existing_model ( config : dict [ str , Any ] , provider : str , default : str ) - > str :
""" Return the last configured model for a provider or a safe baseline. """
model_cfg = config . get ( " model " , { } )
if isinstance ( model_cfg , dict ) and model_cfg . get ( " provider " ) == provider :
return str ( model_cfg . get ( " model " ) or model_cfg . get ( " default " ) or default )
fallbacks = config . get ( " fallback_providers " , [ ] )
if isinstance ( fallbacks , list ) :
for fallback in fallbacks :
if isinstance ( fallback , dict ) and fallback . get ( " provider " ) == provider :
return str ( fallback . get ( " model " ) or default )
return default
2026-08-10 23:28:35 -03:00
def codex_cli_authenticated ( ) - > bool :
""" Return whether the installed Codex CLI has a usable local login. """
codex = shutil . which ( " codex " )
if codex :
try :
status = subprocess . run (
[ codex , " login " , " status " ] ,
capture_output = True ,
check = False ,
text = True ,
timeout = 10 ,
)
detail = f " { status . stdout } \n { status . stderr } " . lower ( )
if status . returncode == 0 and (
" logged in " in detail or " authenticated " in detail
) :
return True
except ( OSError , subprocess . SubprocessError ) :
pass
return False
2026-08-08 17:59:45 -03:00
def discover_codex_models ( ) - > Catalog :
""" Use the authenticated Codex endpoint; catalogs are status-only fallback. """
token = " "
try :
from hermes_cli . auth import resolve_codex_runtime_credentials
credentials = resolve_codex_runtime_credentials ( refresh_if_expiring = True ) or { }
token = str ( credentials . get ( " api_key " ) or " " ) . strip ( )
except Exception :
token = " "
live : list [ str ] = [ ]
if token :
try :
from hermes_cli . codex_models import _fetch_models_from_api
live = _unique_models ( _fetch_models_from_api ( token ) )
except Exception :
live = [ ]
if live :
return Catalog ( " openai-codex " , live , True , True , " connected " )
try :
from hermes_cli . models import provider_model_ids
known = _unique_models ( provider_model_ids ( " openai-codex " , force_refresh = True ) )
except Exception :
known = [ ]
2026-08-10 23:28:35 -03:00
# The owner agent deliberately uses the local Codex app-server and the
# Codex CLI's ChatGPT login. That credential is not exported into Hermes'
# bearer-token store, so API discovery above may be empty even though the
# runtime is fully authenticated. Treat a successful CLI status check as a
# connected, degraded catalog: routing may use Codex while retaining the
# last-known working model names until app-server discovery is available.
if codex_cli_authenticated ( ) :
return Catalog (
" openai-codex " ,
known ,
False ,
True ,
" connected-cli " ,
)
2026-08-08 17:59:45 -03:00
state = " degraded " if token else " not-configured "
return Catalog ( " openai-codex " , known , False , bool ( token ) , state )
def discover_claude_models ( ) - > Catalog :
2026-08-12 23:08:21 -03:00
""" Use the native subscription login before any API-key catalog fallback. """
claude = shutil . which ( " claude " ) or os . environ . get ( " HERMES_CLAUDE_BIN " , " " )
if claude :
try :
environment = os . environ . copy ( )
environment . pop ( " ANTHROPIC_API_KEY " , None )
environment . pop ( " CLAUDE_API_KEY " , None )
status = subprocess . run (
[ claude , " auth " , " status " ] ,
capture_output = True ,
check = False ,
text = True ,
timeout = 15 ,
env = environment ,
)
detail = json . loads ( status . stdout ) if status . stdout . strip ( ) else { }
if (
status . returncode == 0
and isinstance ( detail , dict )
and detail . get ( " loggedIn " ) is True
and detail . get ( " apiProvider " ) == " firstParty "
) :
try :
from hermes_cli . models import provider_model_ids
known = _unique_models (
provider_model_ids ( " anthropic " , force_refresh = True )
)
except Exception :
known = [ ]
return Catalog (
" anthropic " ,
_unique_models ( ( * CLAUDE_SUBSCRIPTION_MODELS , * known ) ) ,
True ,
True ,
" connected-subscription " ,
)
except ( OSError , subprocess . SubprocessError , ValueError , json . JSONDecodeError ) :
pass
2026-08-08 17:59:45 -03:00
try :
from hermes_cli . models import provider_model_ids
known = _unique_models ( provider_model_ids ( " anthropic " , force_refresh = True ) )
except Exception :
known = [ ]
2026-08-15 17:58:47 -03:00
return Catalog ( " anthropic " , known , False , False , " not-configured " )
2026-08-08 17:59:45 -03:00
def _profile_config (
base : dict [ str , Any ] ,
primary_provider : str ,
primary_model : str ,
fallback : dict [ str , str ] ,
effort : str ,
) - > dict [ str , Any ] :
""" Derive a worker configuration with an explicit cross-provider fallback. """
config = copy . deepcopy ( base )
config [ " model " ] = {
" provider " : primary_provider ,
" default " : primary_model ,
" model " : primary_model ,
2026-08-10 22:39:51 -03:00
" openai_runtime " : " codex_app_server " ,
2026-08-08 17:59:45 -03:00
}
2026-08-11 01:14:46 -03:00
config [ " fallback_providers " ] = [ fallback ]
# Local OpenAI-compatible runtimes top out at the equivalent of high.
# Never silently downgrade an explicitly xhigh task after both hosted
# provider lanes are exhausted.
if effort != " xhigh " :
config [ " fallback_providers " ] . append ( copy . deepcopy ( ATLAS_FALLBACK ) )
2026-08-08 17:59:45 -03:00
agent = config . setdefault ( " agent " , { } )
if isinstance ( agent , dict ) :
agent [ " reasoning_effort " ] = effort
config [ " toolsets " ] = [ ]
return config
2026-08-11 20:22:26 -03:00
def _switchyard_profile_config (
base : dict [ str , Any ] , route : str , effort : str
) - > dict [ str , Any ] :
""" Derive a profile that cannot bypass the Switchyard authority. """
config = copy . deepcopy ( base )
providers = config . setdefault ( " providers " , { } )
if not isinstance ( providers , dict ) :
providers = { }
config [ " providers " ] = providers
providers [ SWITCHYARD_PROVIDER ] = {
" name " : " Atlas Switchyard " ,
" api " : SWITCHYARD_API ,
" api_key " : " atlas-switchyard " ,
" default_model " : route ,
" transport " : " chat_completions " ,
}
config [ " model " ] = {
" provider " : SWITCHYARD_PROVIDER ,
" default " : route ,
" model " : route ,
}
config [ " fallback_providers " ] = [ ]
config [ " model_catalog " ] = { " enabled " : True , " ttl_hours " : 1 }
agent = config . setdefault ( " agent " , { } )
if isinstance ( agent , dict ) :
agent [ " reasoning_effort " ] = effort
config [ " toolsets " ] = [ ]
return config
2026-08-08 17:59:45 -03:00
def _write_profile (
root : Path ,
name : str ,
description : str ,
soul : str ,
config : dict [ str , Any ] ,
env_values : dict [ str , str ] ,
) - > None :
""" Create or refresh a managed Hermes worker profile. """
profile = root / " profiles " / name
for directory in ( " logs " , " sessions " , " skills " , " workspace " , " home " ) :
( profile / directory ) . mkdir ( parents = True , exist_ok = True )
_write_yaml ( profile / " config.yaml " , config )
_write_yaml (
profile / " profile.yaml " ,
{ " description " : description , " description_auto " : False } ,
)
_atomic_write ( profile / " SOUL.md " , soul . rstrip ( ) + " \n " )
_update_profile_env ( profile / " .env " , env_values )
2026-08-11 20:22:26 -03:00
def configure_routes (
root : Path ,
codex : Catalog ,
claude : Catalog ,
catalog_path : Path | None = None ,
) - > dict [ str , Any ] :
""" Refresh catalogs while keeping every Hermes profile on Switchyard. """
2026-08-08 17:59:45 -03:00
config_path = root / " config.yaml "
base = _read_yaml ( config_path )
2026-08-11 20:22:26 -03:00
resolved_catalog_path = catalog_path or (
Path ( ROUTING_CATALOG_PATH )
if ROUTING_CATALOG_PATH
else root / " routing-catalog.json "
)
catalog = write_routing_catalog ( resolved_catalog_path , codex , claude )
providers = catalog [ " providers " ]
codex_models = providers [ " codex " ] [ " resolved " ]
claude_models = providers [ " claude " ] [ " resolved " ]
coordinator_toolsets = copy . deepcopy ( base . get ( " toolsets " ) )
base = _switchyard_profile_config ( base , SWITCHYARD_AUTO_ROUTE , " high " )
# The coordinator uses its configured toolsets. Worker profiles below are
# deliberately toolset-empty so Hermes resolves their native defaults.
if coordinator_toolsets is None :
base . pop ( " toolsets " , None )
else :
base [ " toolsets " ] = coordinator_toolsets
2026-08-08 17:59:45 -03:00
_write_yaml ( config_path , base )
env_values = _read_env ( root / " .env " )
routes : dict [ str , list [ str ] ] = { }
for effort in EFFORTS :
codex_name = f " codex- { effort } "
claude_name = f " claude- { effort } "
2026-08-11 20:22:26 -03:00
codex_route = {
" low " : " atlas/manual/codex/luna " ,
" medium " : " atlas/manual/codex/terra " ,
" high " : " atlas/manual/codex/sol " ,
" xhigh " : " atlas/manual/codex/sol " ,
} [ effort ]
claude_route = {
" low " : " atlas/manual/claude/haiku " ,
" medium " : " atlas/manual/claude/sonnet " ,
" high " : " atlas/manual/claude/sonnet " ,
" xhigh " : " atlas/manual/claude/opus " ,
} [ effort ]
2026-08-08 17:59:45 -03:00
_write_profile (
root ,
codex_name ,
2026-08-11 20:22:26 -03:00
f " Codex implementation preference at { effort } effort, enforced by Switchyard. " ,
2026-08-08 17:59:45 -03:00
" You are an implementation worker. Make focused, tested changes for the assigned task, preserve unrelated work, and report evidence and blockers to the coordinator. " ,
2026-08-11 20:22:26 -03:00
_switchyard_profile_config ( base , codex_route , effort ) ,
2026-08-08 17:59:45 -03:00
env_values ,
)
_write_profile (
root ,
claude_name ,
2026-08-11 20:22:26 -03:00
f " Claude analysis preference at { effort } effort, enforced by Switchyard. " ,
2026-08-08 17:59:45 -03:00
" You are an architecture and review worker. Analyze the assigned task deeply, change files only when asked, and return concise conclusions, evidence, and risks to the coordinator. " ,
2026-08-11 20:22:26 -03:00
_switchyard_profile_config ( base , claude_route , effort ) ,
2026-08-08 17:59:45 -03:00
env_values ,
)
2026-08-11 20:22:26 -03:00
routes [ codex_name ] = [ codex_route ]
routes [ claude_name ] = [ claude_route ]
2026-08-08 17:59:45 -03:00
_write_profile (
root ,
" synthesis-xhigh " ,
" Cross-provider synthesis and critical review, capped at xhigh effort. " ,
" Synthesize the worker evidence into one answer. Resolve disagreements explicitly, verify high-risk claims, and never claim completion without cited validation. " ,
2026-08-11 20:22:26 -03:00
_switchyard_profile_config ( base , SWITCHYARD_AUTO_ROUTE , " xhigh " ) ,
2026-08-08 17:59:45 -03:00
env_values ,
)
2026-08-11 20:22:26 -03:00
routes [ " synthesis-xhigh " ] = [ SWITCHYARD_AUTO_ROUTE ]
2026-08-08 17:59:45 -03:00
_write_yaml (
root / " profile.yaml " ,
{
2026-08-11 20:22:26 -03:00
" description " : " Owner-only project coordinator using Switchyard to route Hermes, Codex, Claude, and local model boundaries. " ,
2026-08-08 17:59:45 -03:00
" description_auto " : False ,
} ,
)
2026-08-11 20:22:26 -03:00
routes [ " coordinator " ] = [ SWITCHYARD_AUTO_ROUTE ]
routes [ " catalog " ] = [
* ( f " openai-codex/ { model } " for model in codex_models . values ( ) ) ,
* ( f " anthropic/ { model } " for model in claude_models . values ( ) ) ,
2026-08-08 17:59:45 -03:00
]
return routes