Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,14 @@ Empty `var` = the skill's default behavior (scan everything, auto-pick topics).
The default model for all skills is set in `aeon.yml` (or from the dashboard header dropdown):

```yaml
model: claude-sonnet-4-6
model: claude-sonnet-5
```

Options: `claude-sonnet-4-6` (default), `claude-opus-4-8`, `claude-fable-5`, `claude-opus-4-7`, `claude-sonnet-5`, `claude-haiku-4-5-20251001`. Per-run overrides are available via workflow dispatch, and individual skills can override to optimize cost:
Options: `claude-sonnet-5` (default), `claude-opus-5`, `claude-haiku-4-5-20251001`. Per-run overrides are available via workflow dispatch, and individual skills can override to optimize cost:

```yaml
skills:
token-movers: { enabled: true, schedule: "30 12 * * *", model: "claude-sonnet-4-6" }
token-movers: { enabled: true, schedule: "30 12 * * *", model: "claude-haiku-4-5-20251001" }
```

> These ids apply to the **claude** harness. Each other [harness](../docs/harnesses.md) carries its own model list (Codex `openai/*`, Kimi `moonshotai/*`, Vibe `mistralai/*`, Pi `deepseek/*`), which the dashboard picker swaps in when you select that harness.
Expand Down
9 changes: 3 additions & 6 deletions .github/workflows/aeon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,8 @@ on:
# "Resolve harness" forwards them verbatim as the harness model.
options:
- '(config default)'
- claude-opus-4-8
- claude-opus-4-7
- claude-fable-5
- claude-sonnet-5
- claude-sonnet-4-6
- claude-opus-5
- claude-haiku-4-5-20251001
- grok-4.5
- openai/gpt-5-mini
Expand Down Expand Up @@ -287,7 +284,7 @@ jobs:
# 3. a per-harness cheap default.
# aeon-native ids (claude-*/grok-*) mean nothing to an OpenRouter CLI, so
# they're treated as "unset" and fall through to the default — a repo that
# never touched the model picker (still `model: claude-sonnet-4-6`) still
# never touched the model picker (still `model: claude-sonnet-5`) still
# gets a working default instead of a dead id. The greps carry `|| true`
# for the same reason as the harness greps above (no key ≠ fatal).
# Each harness defaults to its own native family (the dashboard's
Expand Down Expand Up @@ -782,7 +779,7 @@ jobs:
elif [ -n "$SKILL_MODEL" ]; then
MODEL="$SKILL_MODEL"
else
MODEL="${CONFIG_MODEL:-claude-sonnet-4-6}"
MODEL="${CONFIG_MODEL:-claude-sonnet-5}"
fi

# --- Harness resolution (which agent CLI runs the skill) ---
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ jobs:
run: |
TODAY=$(date -u +%Y-%m-%d)
CONFIG_MODEL=$(grep -E '^model:' aeon.yml | sed 's/^model: *//' | tr -d ' ')
MODEL="${CONFIG_MODEL:-claude-sonnet-4-6}"
MODEL="${CONFIG_MODEL:-claude-sonnet-5}"

# Harness resolution — inbound messaging isn't a per-skill run, so it
# follows the repo's global `harness:` in aeon.yml (same default/guard as
Expand Down
14 changes: 8 additions & 6 deletions aeon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Enable skills by setting enabled: true. Multiple skills at the same time run in parallel.
# Set var: "value" to pass a default parameter to the skill.
# Set model: "claude-sonnet-4-6" per skill to override the default model (cost optimization).
# Set model: "claude-haiku-4-5-20251001" per skill to override the default model (cost optimization).
# Set harness: "grok" per skill to run it through the Grok Build CLI instead of the
# default Claude Code harness (see the top-level `harness:` key below).

Expand Down Expand Up @@ -177,14 +177,16 @@ chains:
# - skill: routine, consume: [token-movers, hn-digest, issue-triage, github-trending]

# Default model for all skills. Override per-run via workflow_dispatch.
# Options: claude-opus-4-8, claude-fable-5, claude-sonnet-5, claude-sonnet-4-6, claude-haiku-4-5-20251001
# (Grok harness models: grok-4.5 — flagship reasoning model & grok's current default,
# run with --no-subagents in CI; grok-composer-2.5-fast — fast/cheap single-agent.
# The older grok-build-0.1 is gateway-only, via XAI_API_KEY + the GROK_MODEL var.)
# Options: claude-sonnet-5, claude-opus-5, claude-haiku-4-5-20251001
# (Grok harness model: grok-4.5, the flagship reasoning model and grok's current
# default, run with --no-subagents in CI. It's the only id the grok CLI accepts on an
# X-account login, and it's the gateway default too. The older coding ids
# (grok-build-0.1, grok-composer-2.5-fast, grok-4.3) are gateway-only, reachable via
# XAI_API_KEY + the GROK_MODEL var.)
# The non-claude harnesses use their OWN model ids (codex openai/*, kimi moonshotai/*,
# vibe mistralai/*, pi deepseek/*) — surfaced in the dashboard picker per harness; see
# docs/harnesses.md. The value below only applies to the claude/grok harnesses.
model: claude-sonnet-4-6
model: claude-sonnet-5

# Agent harness — which coding-agent CLI runs your skills.
# claude (default) — Claude Code (`claude -p`); uses the AI Gateway below.
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default function Dashboard() {
const [skills, setSkills] = useState<Skill[]>([])
const [runs, setRuns] = useState<Run[]>([])
const [secrets, setSecrets] = useState<Secret[]>([])
const [model, setModel] = useState('claude-sonnet-4-6')
const [model, setModel] = useState('claude-sonnet-5')
const [harness, setHarness] = useState<Harness>('claude')
const [gateway, setGateway] = useState<GatewayProvider>('auto')
const [repo, setRepo] = useState('')
Expand Down
34 changes: 17 additions & 17 deletions apps/dashboard/lib/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ import {
const MINIMAL_YAML = `skills:
heartbeat: { enabled: true, schedule: "0 12 * * *" }

model: claude-sonnet-4-6
model: claude-sonnet-5
`;

const FULL_YAML = `# Aeon configuration
skills:
morning-brief: { enabled: false, schedule: "0 7 * * *" }
market-pulse: { enabled: true, schedule: "0 12 * * *", model: "claude-sonnet-4-6" }
market-pulse: { enabled: true, schedule: "0 12 * * *", model: "claude-sonnet-5" }
heartbeat: { enabled: true, schedule: "0 12 * * *" }

model: claude-opus-4-8
model: claude-opus-5

gateway:
provider: direct
Expand All @@ -55,7 +55,7 @@ describe("parseConfig", () => {
assert.equal(config.skills["heartbeat"].schedule, "0 12 * * *");
assert.equal(config.skills["heartbeat"].var, "");
assert.equal(config.skills["heartbeat"].model, "");
assert.equal(config.model, "claude-sonnet-4-6");
assert.equal(config.model, "claude-sonnet-5");
});

it("parses a full config with all fields", () => {
Expand All @@ -64,16 +64,16 @@ describe("parseConfig", () => {
assert.equal(config.skills["morning-brief"].enabled, false);
assert.equal(config.skills["morning-brief"].schedule, "0 7 * * *");
assert.equal(config.skills["market-pulse"].enabled, true);
assert.equal(config.skills["market-pulse"].model, "claude-sonnet-4-6");
assert.equal(config.model, "claude-opus-4-8");
assert.equal(config.skills["market-pulse"].model, "claude-sonnet-5");
assert.equal(config.model, "claude-opus-5");
assert.equal(config.gateway.provider, "direct");
assert.equal(config.jsonrenderEnabled, true);
});

it("defaults model to claude-sonnet-4-6 when absent", () => {
it("defaults model to claude-sonnet-5 when absent", () => {
const yaml = `skills:\n test: { enabled: false, schedule: "0 0 * * *" }\n`;
const config = parseConfig(yaml);
assert.equal(config.model, "claude-sonnet-4-6");
assert.equal(config.model, "claude-sonnet-5");
});

it("defaults gateway to auto when absent", () => {
Expand Down Expand Up @@ -106,7 +106,7 @@ describe("parseConfig", () => {

it("handles empty skills section", () => {
// yaml library parses `skills:` with no entries as null, not an empty map
const yaml = `skills:\n\nmodel: claude-sonnet-4-6\n`;
const yaml = `skills:\n\nmodel: claude-sonnet-5\n`;
const config = parseConfig(yaml);
assert.equal(Object.keys(config.skills).length, 0);
});
Expand Down Expand Up @@ -138,8 +138,8 @@ describe("updateSkillInConfig", () => {
});

it("sets a model override on a skill", () => {
const updated = updateSkillInConfig(MINIMAL_YAML, "heartbeat", { model: "claude-sonnet-4-6" });
assert.ok(updated.includes("model: claude-sonnet-4-6") || updated.includes("model: 'claude-sonnet-4-6'"));
const updated = updateSkillInConfig(MINIMAL_YAML, "heartbeat", { model: "claude-sonnet-5" });
assert.ok(updated.includes("model: claude-sonnet-5") || updated.includes("model: 'claude-sonnet-5'"));
});

it("returns original yaml for non-existent skill", () => {
Expand All @@ -164,9 +164,9 @@ describe("updateSkillInConfig", () => {

describe("updateModelInConfig", () => {
it("updates the top-level model", () => {
const updated = updateModelInConfig(MINIMAL_YAML, "claude-opus-4-8");
const updated = updateModelInConfig(MINIMAL_YAML, "claude-opus-5");
const config = parseConfig(updated);
assert.equal(config.model, "claude-opus-4-8");
assert.equal(config.model, "claude-opus-5");
});

it("replaces an existing model", () => {
Expand Down Expand Up @@ -321,7 +321,7 @@ describe("addSkillToConfig", () => {
});

it("inserts at end when heartbeat is absent", () => {
const yaml = `skills:\n alpha: { enabled: false, schedule: "0 0 * * *" }\n\nmodel: claude-sonnet-4-6\n`;
const yaml = `skills:\n alpha: { enabled: false, schedule: "0 0 * * *" }\n\nmodel: claude-sonnet-5\n`;
const updated = addSkillToConfig(yaml, "beta");
const config = parseConfig(updated);
assert.ok(config.skills["beta"]);
Expand Down Expand Up @@ -353,9 +353,9 @@ describe("round-trip config mutations", () => {
});

it("update model and gateway independently", () => {
let yaml = updateModelInConfig(MINIMAL_YAML, "claude-opus-4-8");
let yaml = updateModelInConfig(MINIMAL_YAML, "claude-opus-5");
const config1 = parseConfig(yaml);
assert.equal(config1.model, "claude-opus-4-8");
assert.equal(config1.model, "claude-opus-5");

// Model change should not affect skills
assert.equal(config1.skills["heartbeat"].enabled, true);
Expand Down Expand Up @@ -390,7 +390,7 @@ describe("upsertSkillInConfig", () => {
const config = parseConfig(yaml);
assert.equal(config.skills["market-pulse"].schedule, "0 6 * * *");
// Pre-existing fields survive, and the key appears exactly once.
assert.equal(config.skills["market-pulse"].model, "claude-sonnet-4-6");
assert.equal(config.skills["market-pulse"].model, "claude-sonnet-5");
assert.equal(yaml.match(/^\s*market-pulse:/gm)?.length, 1);
});

Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function parseConfig(raw: string): AeonConfig {
}
}

const model = String(doc.get('model') ?? 'claude-sonnet-4-6')
const model = String(doc.get('model') ?? 'claude-sonnet-5')
const harnessRaw = String(doc.get('harness') ?? 'claude')
const harness: Harness = HARNESSES.find(h => h === harnessRaw) ?? 'claude'

Expand Down
5 changes: 1 addition & 4 deletions apps/dashboard/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import { HARNESS_AUTH } from './harness-auth'
// harness-switch snap uses (modelsForHarness(...)[0] in app/page.tsx). Keep it in
// sync with the config default in lib/config.ts and aeon.yml `model:`.
export const MODELS = [
{ id: 'claude-sonnet-4-6', label: 'Sonnet 4.6' },
{ id: 'claude-opus-4-8', label: 'Opus 4.8' },
{ id: 'claude-opus-4-7', label: 'Opus 4.7' },
{ id: 'claude-fable-5', label: 'Fable 5' },
{ id: 'claude-sonnet-5', label: 'Sonnet 5' },
{ id: 'claude-opus-5', label: 'Opus 5' },
{ id: 'claude-haiku-4-5-20251001', label: 'Haiku 4.5' },
]

Expand Down
2 changes: 1 addition & 1 deletion docs/ADK.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ Everything on-demand goes through one workflow - `aeon.yml` accepts `workflow_di
|---|---|
| `skill` | required; must match `^[a-zA-Z0-9_-]+$` and a directory under `skills/` |
| `var` | the skill's input for this run |
| `model` | override, e.g. `claude-opus-4-8` - must be one of the workflow's `choice` options or GitHub rejects with **422** |
| `model` | override, e.g. `claude-opus-5` - must be one of the workflow's `choice` options or GitHub rejects with **422** |
| `harness` | `claude` (default) or `grok` - see [Harnesses](harnesses.md) |

```ts
Expand Down
2 changes: 1 addition & 1 deletion docs/attestation.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ To read the richer metadata, verify the manifest and inspect its bytes:
```bash
gh attestation verify output/.attest/<skill>-<run_id>.json --repo <owner>/<repo>
cat output/.attest/<skill>-<run_id>.json
# { "skill": "...", "model": "claude-opus-4-8", "mode": "read-only",
# { "skill": "...", "model": "claude-opus-5", "mode": "read-only",
# "trigger": "schedule", "commit": "<sha>", "run_id": "<id>",
# "output": { "path": "output/.attest/<skill>-<run_id>.md", "sha256": "<digest>" } }
```
Expand Down
2 changes: 1 addition & 1 deletion harness-adapter/adapters/pi.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ command -v pi >/dev/null 2>&1 || {
ARGS=(--mode json --no-session --approve)

# model: pi is multi-provider — pass anything through (its registry matches
# patterns like "claude-sonnet-4-6", "openai/gpt-4o", or bare "sonnet")
# patterns like "claude-sonnet-5", "openai/gpt-4o", or bare "sonnet")
[ -n "${RH_MODEL:-}" ] && [ "${RH_MODEL}" != "default" ] && ARGS+=(--model "$RH_MODEL")

# read-only -> tool subsetting (pi's only native lever; advisory without the
Expand Down
39 changes: 22 additions & 17 deletions scripts/llm-gateway.sh
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,14 @@ case "${GATEWAY:-direct}" in
echo "::notice::Routing through Bankr Gateway (https://llm.bankr.bot)"
;;

openrouter) # NATIVE — Anthropic "skin", carries Opus 4.8
openrouter) # NATIVE — Anthropic "skin", carries Opus 5
require_secret OPENROUTER_API_KEY
export ANTHROPIC_BASE_URL="https://openrouter.ai/api" # NOT /api/v1
export ANTHROPIC_AUTH_TOKEN="$OPENROUTER_API_KEY" # Bearer; API_KEY must be blank
unset ANTHROPIC_API_KEY CLAUDE_CODE_OAUTH_TOKEN
# Map EVERY model slot Claude Code uses to OpenRouter slugs (opus/sonnet/haiku).
export ANTHROPIC_DEFAULT_OPUS_MODEL="${OPENROUTER_MODEL:-anthropic/claude-opus-4.8}"
export ANTHROPIC_DEFAULT_SONNET_MODEL="${OPENROUTER_MODEL_SONNET:-anthropic/claude-sonnet-4.6}"
export ANTHROPIC_DEFAULT_OPUS_MODEL="${OPENROUTER_MODEL:-anthropic/claude-opus-5}"
export ANTHROPIC_DEFAULT_SONNET_MODEL="${OPENROUTER_MODEL_SONNET:-anthropic/claude-sonnet-5}"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="${OPENROUTER_MODEL_HAIKU:-anthropic/claude-haiku-4.5}"
MODEL="$ANTHROPIC_DEFAULT_OPUS_MODEL"
echo "::notice::Routing through OpenRouter (Anthropic-native) as ${MODEL}"
Expand All @@ -201,7 +201,7 @@ case "${GATEWAY:-direct}" in
export ANTHROPIC_BASE_URL="https://api.usepod.ai/proxy/${USEPOD_TOKEN}"
export ANTHROPIC_AUTH_TOKEN="unused" # UsePod auths via the path token
unset ANTHROPIC_API_KEY CLAUDE_CODE_OAUTH_TOKEN
# UsePod mirrors the upstream Anthropic surface, so aeon's claude-opus-4-8 id
# UsePod mirrors the upstream Anthropic surface, so aeon's claude-opus-5 id
# is passed through by default. If UsePod needs marketplace-specific ids, set
# USEPOD_MODEL (+ _SONNET / _HAIKU) to override.
if [ -n "${USEPOD_MODEL:-}" ]; then MODEL="$USEPOD_MODEL"; fi
Expand All @@ -218,10 +218,13 @@ case "${GATEWAY:-direct}" in
export ANTHROPIC_BASE_URL="${XAI_ANTHROPIC_BASE_URL:-https://api.x.ai}"
export ANTHROPIC_AUTH_TOKEN="$XAI_API_KEY" # Bearer; API_KEY must be blank
unset ANTHROPIC_API_KEY CLAUDE_CODE_OAUTH_TOKEN
# Pin every model slot to a grok coding model. GROK_MODEL overrides — e.g. set it
# to grok-4.5 (xAI's current flagship, available on api.x.ai) instead of the
# conservative grok-build-0.1 default kept here for backward-compatible behavior.
grok_model="${GROK_MODEL:-grok-build-0.1}"
# Pin every model slot to a grok coding model. Defaults to grok-4.5, xAI's current
# flagship and the same id the grok harness offers (GROK_MODELS in the dashboard's
# constants.ts), so the gateway and CLI paths name the same model. GROK_MODEL
# overrides: the older coding ids (grok-build-0.1, grok-composer-2.5-fast,
# grok-4.3) are api.x.ai model strings that still work here, even though the grok
# CLI rejects them on an X-account OAuth login.
grok_model="${GROK_MODEL:-grok-4.5}"
export ANTHROPIC_DEFAULT_OPUS_MODEL="$grok_model"
export ANTHROPIC_DEFAULT_SONNET_MODEL="$grok_model"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="$grok_model"
Expand All @@ -236,31 +239,33 @@ case "${GATEWAY:-direct}" in
# uses dot-form ids: drop any trailing -YYYYMMDD date, then convert each
# <digit>-<digit> to <digit>.<digit>. SURPLUS_MODEL overrides; opus-4.8 is the
# fallback when $MODEL is unset.
surplus_model="${SURPLUS_MODEL:-$(printf '%s' "${MODEL:-claude-opus-4-8}" | sed -E 's/-[0-9]{8}$//; s/([0-9])-([0-9])/\1.\2/g')}"
surplus_model="${SURPLUS_MODEL:-$(printf '%s' "${MODEL:-claude-opus-5}" | sed -E 's/-[0-9]{8}$//; s/([0-9])-([0-9])/\1.\2/g')}"
start_ccr_sidecar surplus \
"https://www.surplusintelligence.ai/api/inference/v1/chat/completions" \
"$SURPLUS_API_KEY" "$surplus_model"
echo "::notice::Routing through Surplus via claude-code-router (${surplus_model})"
;;

venice) # SIDECAR — OpenAI-compatible (dash-form ids); tops out ~Opus 4.6
venice) # SIDECAR — OpenAI-compatible (dash-form ids); carries Opus 5, no haiku
require_secret VENICE_API_KEY
# Set VENICE_CLEANCACHE=1 to add the cleancache transformer (1h TTL, avoids
# the shared 4-block prompt-cache limit) if you hit cache errors.
# The sidecar pins ONE model, so track aeon's $MODEL — but Venice's catalog
# caps at ~Opus 4.6, so a newer $MODEL (opus 4.7/4.8, fable) would 404. Derive
# only for models Venice is known to carry (dash-form, date suffix stripped);
# anything else keeps the safe opus-4-6 default. VENICE_MODEL overrides.
# (Confirm/extend the allowlist when Venice is live-validated.)
# The sidecar pins ONE model, so track aeon's $MODEL. Venice names models with
# aeon's own dash-form ids, so the picker's ids pass straight through (date
# suffix stripped) when Venice carries them. It carries NO haiku at all, so
# haiku, and anything else off-catalog, falls back to sonnet-5 rather than
# 404ing on a model Venice never had. VENICE_MODEL overrides.
# Allowlist verified against api.venice.ai/api/v1/models on 2026-07-24.
# VENICE_BASE_URL (repo variable) points the sidecar at any Venice-compatible
# endpoint — a self-hosted relay, a billing proxy, a regional mirror — same
# override pattern as VENICE_MODEL. Defaults to Venice's public API.
venice_model="${VENICE_MODEL:-}"
if [ -z "$venice_model" ]; then
m="$(printf '%s' "${MODEL:-}" | sed -E 's/-[0-9]{8}$//')"
case "$m" in
claude-opus-4-6|claude-sonnet-4-6|claude-haiku-4-5) venice_model="$m" ;;
*) venice_model="claude-opus-4-6" ;;
claude-opus-5|claude-sonnet-5|claude-fable-5|claude-opus-4-8|claude-opus-4-7|\
claude-opus-4-6|claude-opus-4-5|claude-sonnet-4-6|claude-sonnet-4-5) venice_model="$m" ;;
*) venice_model="claude-sonnet-5" ;;
esac
fi
start_ccr_sidecar venice \
Expand Down
2 changes: 1 addition & 1 deletion scripts/tests/test_run_grok.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ grep -qx -- "--permission-mode" "$ARGS_FILE" && grep -qx "bypassPermissions" "$A
grep -qx -- "--rules" "$ARGS_FILE" && pass "passes --rules compat preamble" || bad "passes --rules compat preamble"

# 4b. --model is OMITTED for a leftover claude-* id or empty (grok uses its default)
echo "p" | GROK_FAKE_OUT='{"text":"x"}' MODEL=claude-sonnet-4-6 SKILL_MODE=write XAI_API_KEY=xai-test bash "$R" >/dev/null 2>&1
echo "p" | GROK_FAKE_OUT='{"text":"x"}' MODEL=claude-sonnet-5 SKILL_MODE=write XAI_API_KEY=xai-test bash "$R" >/dev/null 2>&1
if grep -qx -- "--model" "$ARGS_FILE"; then bad "omits --model for claude-* id"; else pass "omits --model for claude-* id"; fi
echo "p" | GROK_FAKE_OUT='{"text":"x"}' MODEL="" SKILL_MODE=write XAI_API_KEY=xai-test bash "$R" >/dev/null 2>&1
if grep -qx -- "--model" "$ARGS_FILE"; then bad "omits --model for empty model"; else pass "omits --model for empty model"; fi
Expand Down
Loading