@livx.cc/agentx 0.99.84 → 0.99.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,6 @@
1
1
  // src/models.ts
2
- var MODEL_ALIASES = {
3
- fable: "claude-fable-5",
4
- "fable-5": "claude-fable-5",
5
- opus: "claude-opus-5-5",
6
- "opus-5-5": "claude-opus-5-5",
7
- "opus-5": "claude-opus-5",
8
- "opus-4-8": "claude-opus-4-8",
9
- "opus-4-7": "claude-opus-4-7",
10
- "opus-4-6": "claude-opus-4-6",
11
- sonnet: "claude-sonnet-4-6",
12
- haiku: "claude-haiku-4-5-20251001"
13
- };
2
+ import { claudeModelAliases } from "ai.libx.js";
3
+ var MODEL_ALIASES = { ...claudeModelAliases };
14
4
 
15
5
  // src/directives.ts
16
6
  var VENDOR_SHORTHAND = { cc: "claude-code", cur: "cursor", oa: "openai" };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/models.ts","../src/directives.ts"],"sourcesContent":["/**\n * Model aliasing + mid-conversation switch resolution — pure, dependency-free.\n *\n * Harness-level so every agentx consumer (unclaw, para-chat, …) resolves `[opus]`-style\n * aliases, gates by an allow-list, and announces a switch the same way. Policy (which models\n * a caller may use) and session state (the prior model) stay with the consumer; this module\n * is the shared mechanism.\n */\n\n/** Canonical short aliases → concrete Anthropic model ids. */\nexport const MODEL_ALIASES: Record<string, string> = {\n fable: 'claude-fable-5',\n 'fable-5': 'claude-fable-5',\n opus: 'claude-opus-5-5',\n 'opus-5-5': 'claude-opus-5-5',\n 'opus-5': 'claude-opus-5',\n 'opus-4-8': 'claude-opus-4-8',\n 'opus-4-7': 'claude-opus-4-7',\n 'opus-4-6': 'claude-opus-4-6',\n sonnet: 'claude-sonnet-4-6',\n haiku: 'claude-haiku-4-5-20251001',\n};\n\n/** Resolve an alias to a concrete id. A provider prefix (\"anthropic/\") is preserved; unknown ids pass through. */\nexport function resolveModelAlias(input: string): string {\n const slash = input.indexOf('/');\n const prefix = slash === -1 ? '' : input.slice(0, slash + 1);\n const rest = slash === -1 ? input : input.slice(slash + 1);\n // claude-code's catalogue IS the aliases (claude-code/opus); expanding them yields an unknown id.\n if (prefix === 'claude-code/') return input;\n return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);\n}\n\n/** The provider of a model id (\"cursor/x\" -> \"cursor\"). Provider-specific `providerOptions` are only\n * portable within the same provider — leaking them across is a hard 400.\n *\n * A BARE id is the common case, not the exception: `resolveModelAlias('haiku')` yields\n * `claude-haiku-4-5-20251001` with no prefix (ai.libx.js resolves it fuzzily downstream), so reading\n * only the literal \"/\" made every aliased child look like a provider switch and silently dropped the\n * parent's options. The fix belongs HERE — the alias table must keep emitting the ids ai.libx.js\n * expects, and the comparison is provider identity, not string identity.\n *\n * A bare id we can't attribute is UNKNOWN, not a shared provider: it gets a per-id marker so two\n * different unknown ids never compare equal (returning \"\" for both made them look like one provider\n * and would have leaked one host's options onto the other's model). An unknown id still compares\n * equal to ITSELF, so a child staying on the parent's exact model keeps the parent's options. */\nexport function modelProvider(model: string): string {\n const i = model.indexOf('/');\n if (i !== -1) return model.slice(0, i);\n if (/^claude-/i.test(model)) return 'anthropic';\n return `?${model}`; // unknown: identity-scoped, never equal to a different unknown id\n}\n\n/** Short, human-friendly name for a resolved model id (e.g. \"anthropic/claude-opus-4-8\" → \"opus\"). */\nexport function modelShortLabel(model?: string): string {\n if (!model) return 'default';\n const id = model.split('/').pop()!;\n return id.replace(/^claude-/, '').replace(/-\\d.*$/, '');\n}\n\nexport interface ResolveModelSwitchOpts {\n /** Model requested for this turn (e.g. from an inline directive). Undefined = keep `current`. */\n requested?: string;\n /** The turn's default/current model when nothing is requested. */\n current: string;\n /** The session's prior resolved model — drives change detection + the notice. */\n prior?: string;\n /** If set, `requested` must match one of these (by short label) or it's denied and `current` is kept. */\n allowed?: string[];\n /** Announcement formatter. Default: `_[switching from x to y]_\\n\\n`. */\n format?: (from: string, to: string) => string;\n}\n\nexport interface ModelSwitch {\n /** The model to run this turn. */\n model: string;\n /** True when `requested` was rejected by `allowed` and `model` fell back to `current`. */\n denied: boolean;\n /** Inline notice to surface when the model actually changed vs `prior`, else undefined. */\n notice?: string;\n}\n\n/**\n * Resolve which model a turn runs, given an optional request, an allow-list, and the prior model.\n * Comparisons are by short label so provider-prefixed and dated ids unify\n * (\"anthropic/claude-opus-4-8\" ≡ \"claude-opus-4-8\" ≡ \"opus\").\n */\nexport function resolveModelSwitch(opts: ResolveModelSwitchOpts): ModelSwitch {\n const { requested, current, prior, allowed } = opts;\n if (!requested) return { model: current, denied: false };\n if (allowed && !allowed.some((a) => modelShortLabel(a) === modelShortLabel(requested))) {\n return { model: current, denied: true };\n }\n const fmt = opts.format ?? ((from: string, to: string) => `_[switching from ${from} to ${to}]_\\n\\n`);\n const changed = prior !== undefined && modelShortLabel(prior) !== modelShortLabel(requested);\n return {\n model: requested,\n denied: false,\n notice: changed ? fmt(modelShortLabel(prior!), modelShortLabel(requested)) : undefined,\n };\n}\n","/**\n * Inline prompt directives — `[opus] fix x`, `[cc-opus, 50] …`, `[model:openai/gpt-5.5] …` — pure,\n * dependency-free. A message may open with one or more `[..]` groups that pick the model (and, for\n * hosts that honour them, turns/thinking/effort/engine/resume); the rest is the prompt.\n *\n * Shared by every agentx host (unclaw/Shraga, Bare) so the grammar and its edge cases live once:\n * multi-group, strict mode (bracketed prose stays prose), and an unresolvable EXPLICIT model is\n * reported (`unresolvedModel`) rather than dropped — dropping it ran the turn on the previous model.\n *\n * Model tokens resolve through the alias table and an injected `ModelResolver` (the host's catalog,\n * see `makeModelResolver`). `<vendor>-<model>` / `<vendor>/<model>` shorthands (`cc-opus` →\n * `claude-code/opus`, see `VENDOR_SHORTHAND`) select a model only when they resolve (`[cc-by] …` stays prose).\n */\nimport { MODEL_ALIASES } from './models';\n\nexport interface Directives {\n model?: string;\n turns?: number;\n thinking?: 'adaptive' | 'enabled' | 'disabled';\n effort?: 'low' | 'medium' | 'high' | 'max';\n engine?: string;\n /** Session resume for this conversation (host-defined). */\n resume?: boolean;\n}\n\nexport interface ParsedPrompt {\n prompt: string;\n directives: Directives;\n /** An EXPLICIT model selection nothing could resolve. The caller must fail the turn, never run the old model. */\n unresolvedModel?: string;\n /** When `unresolvedModel` matched several catalog ids: the candidates (use the full id). */\n ambiguous?: string[];\n}\n\nexport type ModelHit = { model: string; engine?: string };\n/** Resolves a model token against a host catalog. `{ ambiguous }` = several ids match; never guess. */\nexport type ModelResolver = (token: string) => ModelHit | { ambiguous: string[] } | null;\n\n/** Vendor shorthand → provider id: `[cc-opus]` / `[cc/opus]` = `claude-code/opus`. Extend per host via options. */\nexport const VENDOR_SHORTHAND: Record<string, string> = { cc: 'claude-code', cur: 'cursor', oa: 'openai' };\n\nexport const DIRECTIVE_KEYS = ['model', 'turns', 'thinking', 'think', 'effort', 'engine', 'resume'];\n\nexport interface DirectiveOptions {\n /** Host catalog lookup. Default: the one set by `setModelResolver` (none). */\n resolveModel: ModelResolver | null;\n /** Try the catalog before the alias table (Bare: `[opus]` = the catalog's `claude-code/opus`). */\n resolverFirst: boolean;\n aliases: Record<string, string>;\n vendors: Record<string, string>;\n /** Keys this host honours; positional turns/thinking count only when `turns`/`thinking` are listed. */\n keys: string[];\n /** Every group, the first included, must look like directives (else it stays prompt text). */\n strict: boolean;\n /** Resolve to catalog ids ONLY (Bare): no alias-table fallback, a full id the catalog lacks is refused, and a\n * bare word matches an id's tail only when it is a known alias or carries a digit (`[auto]` stays prose). */\n catalogOnly: boolean;\n warn: (msg: string) => void;\n}\n\nlet defaultResolver: ModelResolver | null = null;\n/** Process-wide default resolver (Shraga's engine registry injects it once). Per-call options override. */\nexport function setModelResolver(fn: ModelResolver | null): void { defaultResolver = fn; }\n\nconst opt = (o?: Partial<DirectiveOptions>): DirectiveOptions => ({\n resolveModel: defaultResolver, resolverFirst: false, aliases: MODEL_ALIASES, vendors: VENDOR_SHORTHAND,\n keys: DIRECTIVE_KEYS, strict: false, catalogOnly: false, warn: (m) => console.warn(m), ...o,\n});\n\n// A group ends at whitespace or the end: `[opus](http://x)` is a markdown link, not a directive.\nconst DIRECTIVE_RE = /^\\s*\\[([^\\]]*)\\](?=\\s|$)\\s*([\\s\\S]*)/;\nconst isQualifiedModel = (v: string) => /^[a-z0-9._-]+\\/[a-z0-9./_-]+$/.test(v);\n\n/** `cc-opus` / `cc/opus` → `claude-code/opus`; a full `claude-code/opus` is returned as is. Else null. */\nexport function expandVendor(token: string, vendors: Record<string, string> = VENDOR_SHORTHAND): string | null {\n const v = token.trim().toLowerCase();\n const m = v.match(/^([a-z0-9]+)[-/](.+)$/);\n if (m && vendors[m[1]]) return `${vendors[m[1]]}/${m[2]}`;\n const slash = v.indexOf('/');\n return slash > 0 && Object.values(vendors).includes(v.slice(0, slash)) ? v : null;\n}\n\n/** Resolve one model token: vendor shorthand → catalog exact id; else alias table + catalog in the configured order. */\nexport function resolveModelToken(token: string, o?: Partial<DirectiveOptions>): ModelHit | { ambiguous: string[] } | null {\n const p = opt(o);\n const v = token.trim().toLowerCase();\n const vendored = expandVendor(v, p.vendors);\n if (vendored) return p.resolveModel ? p.resolveModel(vendored) : p.catalogOnly ? null : { model: vendored }; // no catalog: trust the explicit id\n if (p.catalogOnly) return v.includes('/') || p.aliases[v] || /\\d/.test(v) ? p.resolveModel?.(v) ?? null : null;\n const alias = p.aliases[v] ? { model: p.aliases[v] } : null;\n if (!p.resolverFirst) return alias ?? p.resolveModel?.(v) ?? null;\n const hit = p.resolveModel?.(v) ?? null;\n return hit && 'model' in hit ? hit : alias ?? hit;\n}\n\nconst THINK = ['think', 'adaptive', 'nothink', 'nothinking'];\n\n/** Does a bracket group look like directives (vs. prompt text that happens to start with `[`)? */\nexport function isDirectiveGroup(raw: string, o?: Partial<DirectiveOptions>): boolean {\n const p = opt(o);\n const tokens = raw.split(',').map((t) => t.trim()).filter(Boolean);\n if (!tokens.length) return false;\n return tokens.every((t) => {\n const i = t.indexOf(':');\n if (i !== -1) return p.keys.includes(t.slice(0, i).trim().toLowerCase());\n const v = t.toLowerCase();\n return !!resolveModelToken(v, p) || (p.catalogOnly && !!p.aliases[v])\n || (p.keys.includes('turns') && /^\\d+$/.test(v)) || (p.keys.includes('thinking') && THINK.includes(v));\n });\n}\n\n/** Splits the leading `[..]` directive groups off a prompt — EVERY consecutive leading group. */\nexport function splitDirectiveGroups(text: string, o?: Partial<DirectiveOptions>): { groups: string[]; body: string } {\n const p = opt(o);\n let rest = text;\n const groups: string[] = [];\n for (;;) {\n const m = rest.match(DIRECTIVE_RE);\n if (!m) break;\n const g = m[1].trim();\n if ((groups.length || p.strict) && g && !isDirectiveGroup(g, p)) break;\n groups.push(g);\n rest = m[2];\n }\n return { groups, body: rest };\n}\n\nexport function parseDirectives(text: string, o?: Partial<DirectiveOptions>): ParsedPrompt {\n const p = opt(o);\n const { groups, body } = splitDirectiveGroups(text, p);\n if (!groups.length) return { prompt: text, directives: {} };\n const prompt = body.trim();\n const raw = groups.filter(Boolean).join(',');\n if (!raw) return { prompt, directives: {} };\n\n const d: Directives = {};\n const out: ParsedPrompt = { prompt, directives: d };\n const tokens = raw.split(',');\n // A keyed sibling proves the group is directives, so an unresolvable leading model there IS a dropped selection.\n const proven = tokens.some((t) => { const i = t.indexOf(':'); return i !== -1 && p.keys.includes(t.slice(0, i).trim().toLowerCase()); });\n const fail = (tok: string, hit: ReturnType<typeof resolveModelToken>) => {\n out.unresolvedModel ??= tok;\n if (hit && 'ambiguous' in hit) out.ambiguous ??= hit.ambiguous;\n };\n const setModel = (val: string): boolean | ReturnType<typeof resolveModelToken> => {\n const hit = resolveModelToken(val, p);\n if (!hit || !('model' in hit)) return hit;\n d.model = hit.model;\n if (hit.engine && !d.engine) d.engine = hit.engine; // an engine-owned model implies its engine, never over `[engine:x]`\n return true;\n };\n\n let pos = 0;\n for (const token of tokens) {\n const t = token.trim();\n if (!t) continue;\n const i = t.indexOf(':');\n if (i !== -1) {\n const key = t.slice(0, i).trim().toLowerCase(), val = t.slice(i + 1).trim().toLowerCase();\n if (!p.keys.includes(key)) { p.warn(`[directives] Unknown directive key: \"${key}\"`); continue; }\n if (key === 'model') {\n const r = setModel(val);\n if (r === true) continue;\n // A plain `provider/model` with no catalog to check is concrete — the engine owns the verdict.\n if (!r && isQualifiedModel(val) && !p.catalogOnly) { d.model = val; continue; }\n fail(val, r as any);\n } else applyKey(d, key, val, p.warn);\n continue;\n }\n const val = t.toLowerCase();\n const r = pos === 0 ? setModel(val) : null;\n if (r === true) { /* model */ }\n else if (pos <= 1 && /^\\d+$/.test(val) && p.keys.includes('turns')) d.turns = parseInt(val, 10);\n else if (['think', 'adaptive'].includes(val) && p.keys.includes('thinking')) d.thinking = 'adaptive';\n else if (['nothink', 'nothinking'].includes(val) && p.keys.includes('thinking')) d.thinking = 'disabled';\n else if (pos === 0) {\n // Ambiguous tokens (and, catalog-only, a known alias the catalog lacks) are model selections; a plain unknown one\n // only with a keyed sibling. An unresolved vendor form (`[cc-by]`) is not a selection.\n if (r || (p.catalogOnly && p.aliases[val]) || (proven && !isQualifiedModel(val))) fail(t, r as any);\n p.warn(`[directives] Unknown model: \"${t}\"`);\n }\n pos++;\n }\n return out;\n}\n\nfunction applyKey(d: Directives, key: string, val: string, warn: (m: string) => void): void {\n switch (key) {\n case 'turns': if (/^\\d+$/.test(val)) d.turns = parseInt(val, 10); else warn(`[directives] Invalid turns value: \"${val}\"`); break;\n case 'thinking':\n case 'think': if (['adaptive', 'enabled', 'disabled'].includes(val)) d.thinking = val as Directives['thinking']; else warn(`[directives] Invalid thinking value: \"${val}\"`); break;\n case 'effort': if (['low', 'medium', 'high', 'max'].includes(val)) d.effort = val as Directives['effort']; else warn(`[directives] Invalid effort value: \"${val}\"`); break;\n case 'engine': d.engine = val; break;\n case 'resume':\n if (['on', 'true', '1'].includes(val)) d.resume = true;\n else if (['off', 'false', '0'].includes(val)) d.resume = false;\n else warn(`[directives] Invalid resume value: \"${val}\"`);\n break;\n }\n}\n\n/** Engine/provider → the model ids it advertises. */\nexport interface EngineModels { name: string; models: { value: string }[] }\n\n/**\n * A resolver over host catalogs: exact id first, then the bare suffix after the provider prefix —\n * ONLY when unambiguous across engines (`{ ambiguous }` otherwise, never the first match).\n */\nexport function makeModelResolver(engines: () => EngineModels[]): ModelResolver {\n return (token) => {\n const t = token.toLowerCase();\n const hits: ModelHit[] = [];\n for (const { name, models } of engines()) {\n for (const m of models) {\n if (!m.value) continue;\n const v = m.value.toLowerCase();\n if (v === t) return { model: m.value, engine: name };\n if (v.slice(v.lastIndexOf('/') + 1) === t && !hits.some((h) => h.model === m.value)) hits.push({ model: m.value, engine: name });\n }\n }\n if (hits.length === 1) return hits[0];\n return hits.length > 1 ? { ambiguous: hits.map((h) => h.model) } : null;\n };\n}\n"],"mappings":"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","names":["val","r"]}
1
+ {"version":3,"sources":["../src/models.ts","../src/directives.ts"],"sourcesContent":["/**\n * Model aliasing + mid-conversation switch resolution — pure; aliases come from ai.libx.js's catalog.\n *\n * Harness-level so every agentx consumer (unclaw, para-chat, …) resolves `[opus]`-style\n * aliases, gates by an allow-list, and announces a switch the same way. Policy (which models\n * a caller may use) and session state (the prior model) stay with the consumer; this module\n * is the shared mechanism.\n */\nimport { claudeModelAliases } from 'ai.libx.js';\n\n/** Canonical short aliases → concrete Anthropic model ids — DERIVED from ai.libx.js's catalog (newest entry per\n * family, plus versioned `opus-5`-style aliases). Never hand-pin an id here: a new model = one catalog entry. */\nexport const MODEL_ALIASES: Record<string, string> = { ...claudeModelAliases };\n\n/** Newest catalog model of a Claude family as a provider-qualified id (`'sonnet'` → `'anthropic/claude-sonnet-5-5'`).\n * Use this for every default/tier — a literal dated id goes stale the day the next model ships. */\nexport function latestClaude(family: 'fable' | 'opus' | 'sonnet' | 'haiku', provider = 'anthropic'): string {\n const id = MODEL_ALIASES[family];\n if (!id) throw new Error(`ai.libx.js catalog has no Claude ${family} model`);\n return `${provider}/${id}`;\n}\n\n/** Resolve an alias to a concrete id. A provider prefix (\"anthropic/\") is preserved; unknown ids pass through. */\nexport function resolveModelAlias(input: string): string {\n const slash = input.indexOf('/');\n const prefix = slash === -1 ? '' : input.slice(0, slash + 1);\n const rest = slash === -1 ? input : input.slice(slash + 1);\n // claude-code's catalogue IS the aliases (claude-code/opus); expanding them yields an unknown id.\n if (prefix === 'claude-code/') return input;\n return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);\n}\n\n/** The provider of a model id (\"cursor/x\" -> \"cursor\"). Provider-specific `providerOptions` are only\n * portable within the same provider — leaking them across is a hard 400.\n *\n * A BARE id is the common case, not the exception: `resolveModelAlias('haiku')` yields\n * `claude-haiku-5-5` with no prefix (ai.libx.js resolves it fuzzily downstream), so reading\n * only the literal \"/\" made every aliased child look like a provider switch and silently dropped the\n * parent's options. The fix belongs HERE — the alias table must keep emitting the ids ai.libx.js\n * expects, and the comparison is provider identity, not string identity.\n *\n * A bare id we can't attribute is UNKNOWN, not a shared provider: it gets a per-id marker so two\n * different unknown ids never compare equal (returning \"\" for both made them look like one provider\n * and would have leaked one host's options onto the other's model). An unknown id still compares\n * equal to ITSELF, so a child staying on the parent's exact model keeps the parent's options. */\nexport function modelProvider(model: string): string {\n const i = model.indexOf('/');\n if (i !== -1) return model.slice(0, i);\n if (/^claude-/i.test(model)) return 'anthropic';\n return `?${model}`; // unknown: identity-scoped, never equal to a different unknown id\n}\n\n/** Short, human-friendly name for a resolved model id (e.g. \"anthropic/claude-opus-4-8\" → \"opus\"). */\nexport function modelShortLabel(model?: string): string {\n if (!model) return 'default';\n const id = model.split('/').pop()!;\n return id.replace(/^claude-/, '').replace(/-\\d.*$/, '');\n}\n\nexport interface ResolveModelSwitchOpts {\n /** Model requested for this turn (e.g. from an inline directive). Undefined = keep `current`. */\n requested?: string;\n /** The turn's default/current model when nothing is requested. */\n current: string;\n /** The session's prior resolved model — drives change detection + the notice. */\n prior?: string;\n /** If set, `requested` must match one of these (by short label) or it's denied and `current` is kept. */\n allowed?: string[];\n /** Announcement formatter. Default: `_[switching from x to y]_\\n\\n`. */\n format?: (from: string, to: string) => string;\n}\n\nexport interface ModelSwitch {\n /** The model to run this turn. */\n model: string;\n /** True when `requested` was rejected by `allowed` and `model` fell back to `current`. */\n denied: boolean;\n /** Inline notice to surface when the model actually changed vs `prior`, else undefined. */\n notice?: string;\n}\n\n/**\n * Resolve which model a turn runs, given an optional request, an allow-list, and the prior model.\n * Comparisons are by short label so provider-prefixed and dated ids unify\n * (\"anthropic/claude-opus-4-8\" ≡ \"claude-opus-4-8\" ≡ \"opus\").\n */\nexport function resolveModelSwitch(opts: ResolveModelSwitchOpts): ModelSwitch {\n const { requested, current, prior, allowed } = opts;\n if (!requested) return { model: current, denied: false };\n if (allowed && !allowed.some((a) => modelShortLabel(a) === modelShortLabel(requested))) {\n return { model: current, denied: true };\n }\n const fmt = opts.format ?? ((from: string, to: string) => `_[switching from ${from} to ${to}]_\\n\\n`);\n const changed = prior !== undefined && modelShortLabel(prior) !== modelShortLabel(requested);\n return {\n model: requested,\n denied: false,\n notice: changed ? fmt(modelShortLabel(prior!), modelShortLabel(requested)) : undefined,\n };\n}\n","/**\n * Inline prompt directives — `[opus] fix x`, `[cc-opus, 50] …`, `[model:openai/gpt-5.5] …` — pure,\n * dependency-free. A message may open with one or more `[..]` groups that pick the model (and, for\n * hosts that honour them, turns/thinking/effort/engine/resume); the rest is the prompt.\n *\n * Shared by every agentx host (unclaw/Shraga, Bare) so the grammar and its edge cases live once:\n * multi-group, strict mode (bracketed prose stays prose), and an unresolvable EXPLICIT model is\n * reported (`unresolvedModel`) rather than dropped — dropping it ran the turn on the previous model.\n *\n * Model tokens resolve through the alias table and an injected `ModelResolver` (the host's catalog,\n * see `makeModelResolver`). `<vendor>-<model>` / `<vendor>/<model>` shorthands (`cc-opus` →\n * `claude-code/opus`, see `VENDOR_SHORTHAND`) select a model only when they resolve (`[cc-by] …` stays prose).\n */\nimport { MODEL_ALIASES } from './models';\n\nexport interface Directives {\n model?: string;\n turns?: number;\n thinking?: 'adaptive' | 'enabled' | 'disabled';\n effort?: 'low' | 'medium' | 'high' | 'max';\n engine?: string;\n /** Session resume for this conversation (host-defined). */\n resume?: boolean;\n}\n\nexport interface ParsedPrompt {\n prompt: string;\n directives: Directives;\n /** An EXPLICIT model selection nothing could resolve. The caller must fail the turn, never run the old model. */\n unresolvedModel?: string;\n /** When `unresolvedModel` matched several catalog ids: the candidates (use the full id). */\n ambiguous?: string[];\n}\n\nexport type ModelHit = { model: string; engine?: string };\n/** Resolves a model token against a host catalog. `{ ambiguous }` = several ids match; never guess. */\nexport type ModelResolver = (token: string) => ModelHit | { ambiguous: string[] } | null;\n\n/** Vendor shorthand → provider id: `[cc-opus]` / `[cc/opus]` = `claude-code/opus`. Extend per host via options. */\nexport const VENDOR_SHORTHAND: Record<string, string> = { cc: 'claude-code', cur: 'cursor', oa: 'openai' };\n\nexport const DIRECTIVE_KEYS = ['model', 'turns', 'thinking', 'think', 'effort', 'engine', 'resume'];\n\nexport interface DirectiveOptions {\n /** Host catalog lookup. Default: the one set by `setModelResolver` (none). */\n resolveModel: ModelResolver | null;\n /** Try the catalog before the alias table (Bare: `[opus]` = the catalog's `claude-code/opus`). */\n resolverFirst: boolean;\n aliases: Record<string, string>;\n vendors: Record<string, string>;\n /** Keys this host honours; positional turns/thinking count only when `turns`/`thinking` are listed. */\n keys: string[];\n /** Every group, the first included, must look like directives (else it stays prompt text). */\n strict: boolean;\n /** Resolve to catalog ids ONLY (Bare): no alias-table fallback, a full id the catalog lacks is refused, and a\n * bare word matches an id's tail only when it is a known alias or carries a digit (`[auto]` stays prose). */\n catalogOnly: boolean;\n warn: (msg: string) => void;\n}\n\nlet defaultResolver: ModelResolver | null = null;\n/** Process-wide default resolver (Shraga's engine registry injects it once). Per-call options override. */\nexport function setModelResolver(fn: ModelResolver | null): void { defaultResolver = fn; }\n\nconst opt = (o?: Partial<DirectiveOptions>): DirectiveOptions => ({\n resolveModel: defaultResolver, resolverFirst: false, aliases: MODEL_ALIASES, vendors: VENDOR_SHORTHAND,\n keys: DIRECTIVE_KEYS, strict: false, catalogOnly: false, warn: (m) => console.warn(m), ...o,\n});\n\n// A group ends at whitespace or the end: `[opus](http://x)` is a markdown link, not a directive.\nconst DIRECTIVE_RE = /^\\s*\\[([^\\]]*)\\](?=\\s|$)\\s*([\\s\\S]*)/;\nconst isQualifiedModel = (v: string) => /^[a-z0-9._-]+\\/[a-z0-9./_-]+$/.test(v);\n\n/** `cc-opus` / `cc/opus` → `claude-code/opus`; a full `claude-code/opus` is returned as is. Else null. */\nexport function expandVendor(token: string, vendors: Record<string, string> = VENDOR_SHORTHAND): string | null {\n const v = token.trim().toLowerCase();\n const m = v.match(/^([a-z0-9]+)[-/](.+)$/);\n if (m && vendors[m[1]]) return `${vendors[m[1]]}/${m[2]}`;\n const slash = v.indexOf('/');\n return slash > 0 && Object.values(vendors).includes(v.slice(0, slash)) ? v : null;\n}\n\n/** Resolve one model token: vendor shorthand → catalog exact id; else alias table + catalog in the configured order. */\nexport function resolveModelToken(token: string, o?: Partial<DirectiveOptions>): ModelHit | { ambiguous: string[] } | null {\n const p = opt(o);\n const v = token.trim().toLowerCase();\n const vendored = expandVendor(v, p.vendors);\n if (vendored) return p.resolveModel ? p.resolveModel(vendored) : p.catalogOnly ? null : { model: vendored }; // no catalog: trust the explicit id\n if (p.catalogOnly) return v.includes('/') || p.aliases[v] || /\\d/.test(v) ? p.resolveModel?.(v) ?? null : null;\n const alias = p.aliases[v] ? { model: p.aliases[v] } : null;\n if (!p.resolverFirst) return alias ?? p.resolveModel?.(v) ?? null;\n const hit = p.resolveModel?.(v) ?? null;\n return hit && 'model' in hit ? hit : alias ?? hit;\n}\n\nconst THINK = ['think', 'adaptive', 'nothink', 'nothinking'];\n\n/** Does a bracket group look like directives (vs. prompt text that happens to start with `[`)? */\nexport function isDirectiveGroup(raw: string, o?: Partial<DirectiveOptions>): boolean {\n const p = opt(o);\n const tokens = raw.split(',').map((t) => t.trim()).filter(Boolean);\n if (!tokens.length) return false;\n return tokens.every((t) => {\n const i = t.indexOf(':');\n if (i !== -1) return p.keys.includes(t.slice(0, i).trim().toLowerCase());\n const v = t.toLowerCase();\n return !!resolveModelToken(v, p) || (p.catalogOnly && !!p.aliases[v])\n || (p.keys.includes('turns') && /^\\d+$/.test(v)) || (p.keys.includes('thinking') && THINK.includes(v));\n });\n}\n\n/** Splits the leading `[..]` directive groups off a prompt — EVERY consecutive leading group. */\nexport function splitDirectiveGroups(text: string, o?: Partial<DirectiveOptions>): { groups: string[]; body: string } {\n const p = opt(o);\n let rest = text;\n const groups: string[] = [];\n for (;;) {\n const m = rest.match(DIRECTIVE_RE);\n if (!m) break;\n const g = m[1].trim();\n if ((groups.length || p.strict) && g && !isDirectiveGroup(g, p)) break;\n groups.push(g);\n rest = m[2];\n }\n return { groups, body: rest };\n}\n\nexport function parseDirectives(text: string, o?: Partial<DirectiveOptions>): ParsedPrompt {\n const p = opt(o);\n const { groups, body } = splitDirectiveGroups(text, p);\n if (!groups.length) return { prompt: text, directives: {} };\n const prompt = body.trim();\n const raw = groups.filter(Boolean).join(',');\n if (!raw) return { prompt, directives: {} };\n\n const d: Directives = {};\n const out: ParsedPrompt = { prompt, directives: d };\n const tokens = raw.split(',');\n // A keyed sibling proves the group is directives, so an unresolvable leading model there IS a dropped selection.\n const proven = tokens.some((t) => { const i = t.indexOf(':'); return i !== -1 && p.keys.includes(t.slice(0, i).trim().toLowerCase()); });\n const fail = (tok: string, hit: ReturnType<typeof resolveModelToken>) => {\n out.unresolvedModel ??= tok;\n if (hit && 'ambiguous' in hit) out.ambiguous ??= hit.ambiguous;\n };\n const setModel = (val: string): boolean | ReturnType<typeof resolveModelToken> => {\n const hit = resolveModelToken(val, p);\n if (!hit || !('model' in hit)) return hit;\n d.model = hit.model;\n if (hit.engine && !d.engine) d.engine = hit.engine; // an engine-owned model implies its engine, never over `[engine:x]`\n return true;\n };\n\n let pos = 0;\n for (const token of tokens) {\n const t = token.trim();\n if (!t) continue;\n const i = t.indexOf(':');\n if (i !== -1) {\n const key = t.slice(0, i).trim().toLowerCase(), val = t.slice(i + 1).trim().toLowerCase();\n if (!p.keys.includes(key)) { p.warn(`[directives] Unknown directive key: \"${key}\"`); continue; }\n if (key === 'model') {\n const r = setModel(val);\n if (r === true) continue;\n // A plain `provider/model` with no catalog to check is concrete — the engine owns the verdict.\n if (!r && isQualifiedModel(val) && !p.catalogOnly) { d.model = val; continue; }\n fail(val, r as any);\n } else applyKey(d, key, val, p.warn);\n continue;\n }\n const val = t.toLowerCase();\n const r = pos === 0 ? setModel(val) : null;\n if (r === true) { /* model */ }\n else if (pos <= 1 && /^\\d+$/.test(val) && p.keys.includes('turns')) d.turns = parseInt(val, 10);\n else if (['think', 'adaptive'].includes(val) && p.keys.includes('thinking')) d.thinking = 'adaptive';\n else if (['nothink', 'nothinking'].includes(val) && p.keys.includes('thinking')) d.thinking = 'disabled';\n else if (pos === 0) {\n // Ambiguous tokens (and, catalog-only, a known alias the catalog lacks) are model selections; a plain unknown one\n // only with a keyed sibling. An unresolved vendor form (`[cc-by]`) is not a selection.\n if (r || (p.catalogOnly && p.aliases[val]) || (proven && !isQualifiedModel(val))) fail(t, r as any);\n p.warn(`[directives] Unknown model: \"${t}\"`);\n }\n pos++;\n }\n return out;\n}\n\nfunction applyKey(d: Directives, key: string, val: string, warn: (m: string) => void): void {\n switch (key) {\n case 'turns': if (/^\\d+$/.test(val)) d.turns = parseInt(val, 10); else warn(`[directives] Invalid turns value: \"${val}\"`); break;\n case 'thinking':\n case 'think': if (['adaptive', 'enabled', 'disabled'].includes(val)) d.thinking = val as Directives['thinking']; else warn(`[directives] Invalid thinking value: \"${val}\"`); break;\n case 'effort': if (['low', 'medium', 'high', 'max'].includes(val)) d.effort = val as Directives['effort']; else warn(`[directives] Invalid effort value: \"${val}\"`); break;\n case 'engine': d.engine = val; break;\n case 'resume':\n if (['on', 'true', '1'].includes(val)) d.resume = true;\n else if (['off', 'false', '0'].includes(val)) d.resume = false;\n else warn(`[directives] Invalid resume value: \"${val}\"`);\n break;\n }\n}\n\n/** Engine/provider → the model ids it advertises. */\nexport interface EngineModels { name: string; models: { value: string }[] }\n\n/**\n * A resolver over host catalogs: exact id first, then the bare suffix after the provider prefix —\n * ONLY when unambiguous across engines (`{ ambiguous }` otherwise, never the first match).\n */\nexport function makeModelResolver(engines: () => EngineModels[]): ModelResolver {\n return (token) => {\n const t = token.toLowerCase();\n const hits: ModelHit[] = [];\n for (const { name, models } of engines()) {\n for (const m of models) {\n if (!m.value) continue;\n const v = m.value.toLowerCase();\n if (v === t) return { model: m.value, engine: name };\n if (v.slice(v.lastIndexOf('/') + 1) === t && !hits.some((h) => h.model === m.value)) hits.push({ model: m.value, engine: name });\n }\n }\n if (hits.length === 1) return hits[0];\n return hits.length > 1 ? { ambiguous: hits.map((h) => h.model) } : null;\n };\n}\n"],"mappings":"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","names":["val","r"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as AgentOptions, H as Hooks, n as RunResult, A as Agent, g as PermissionPolicy } from './Agent-RHsiEAiP.js';
2
2
  export { C as ChatFragment, b as Classification, c as Classifier, d as CompactResult, D as DEFAULT_MUTATING, e as Decision, M as ModelRouter, f as ModelRouterOptions, P as PermissionOptions, h as PermissionRule, i as PreToolUseDecision, R as ReasoningEffort, j as RecordingHooks, k as RecordingLifecycle, l as RouteDecision, m as RouterState, T as Tier, o as ToolUse, p as ToolUseMeta, q as claudeCodeDisallowedTools, r as composeHooks, s as estimateTokens, t as jevAvailable, u as jevClassifier, v as pickTier, w as planMode, x as reasoningToChatFragment } from './Agent-RHsiEAiP.js';
3
- export { MODEL_ALIASES, ModelSwitch, ResolveModelSwitchOpts, modelShortLabel, resolveModelAlias, resolveModelSwitch } from './models.js';
3
+ export { MODEL_ALIASES, ModelSwitch, ResolveModelSwitchOpts, latestClaude, modelShortLabel, resolveModelAlias, resolveModelSwitch } from './models.js';
4
4
  import { IFilesystem, FileMetadata } from '@livx.cc/wcli/core';
5
5
  export { CommandExecutor, FileMetadata, IFilesystem, IndexedDbFilesystem, MemFilesystem, registerHeadlessCommands } from '@livx.cc/wcli/core';
6
6
  import { BodDB } from '@bod.ee/db';
@@ -562,7 +562,7 @@ interface WebSearchOptions {
562
562
  description?: string;
563
563
  /** Anthropic key for provider 'anthropic' (default: process.env.ANTHROPIC_API_KEY). */
564
564
  anthropicApiKey?: string;
565
- /** Model for provider 'anthropic' (default 'claude-haiku-4-5'). */
565
+ /** Model for provider 'anthropic' (default: the newest Haiku in the catalog). */
566
566
  model?: string;
567
567
  }
568
568
  interface SearchHit {
package/dist/index.js CHANGED
@@ -974,6 +974,55 @@ var init_todo = __esm({
974
974
  }
975
975
  });
976
976
 
977
+ // src/models.ts
978
+ import { claudeModelAliases } from "ai.libx.js";
979
+ function latestClaude(family, provider = "anthropic") {
980
+ const id = MODEL_ALIASES[family];
981
+ if (!id) throw new Error(`ai.libx.js catalog has no Claude ${family} model`);
982
+ return `${provider}/${id}`;
983
+ }
984
+ function resolveModelAlias(input) {
985
+ const slash = input.indexOf("/");
986
+ const prefix = slash === -1 ? "" : input.slice(0, slash + 1);
987
+ const rest = slash === -1 ? input : input.slice(slash + 1);
988
+ if (prefix === "claude-code/") return input;
989
+ return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);
990
+ }
991
+ function modelProvider(model) {
992
+ const i = model.indexOf("/");
993
+ if (i !== -1) return model.slice(0, i);
994
+ if (/^claude-/i.test(model)) return "anthropic";
995
+ return `?${model}`;
996
+ }
997
+ function modelShortLabel(model) {
998
+ if (!model) return "default";
999
+ const id = model.split("/").pop();
1000
+ return id.replace(/^claude-/, "").replace(/-\d.*$/, "");
1001
+ }
1002
+ function resolveModelSwitch(opts) {
1003
+ const { requested, current, prior, allowed } = opts;
1004
+ if (!requested) return { model: current, denied: false };
1005
+ if (allowed && !allowed.some((a) => modelShortLabel(a) === modelShortLabel(requested))) {
1006
+ return { model: current, denied: true };
1007
+ }
1008
+ const fmt = opts.format ?? ((from, to) => `_[switching from ${from} to ${to}]_
1009
+
1010
+ `);
1011
+ const changed = prior !== void 0 && modelShortLabel(prior) !== modelShortLabel(requested);
1012
+ return {
1013
+ model: requested,
1014
+ denied: false,
1015
+ notice: changed ? fmt(modelShortLabel(prior), modelShortLabel(requested)) : void 0
1016
+ };
1017
+ }
1018
+ var MODEL_ALIASES;
1019
+ var init_models = __esm({
1020
+ "src/models.ts"() {
1021
+ "use strict";
1022
+ MODEL_ALIASES = { ...claudeModelAliases };
1023
+ }
1024
+ });
1025
+
977
1026
  // src/tools.web.ts
978
1027
  function htmlToText(html) {
979
1028
  let s = html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<title[\s\S]*?<\/title>/gi, " ").replace(/<noscript[\s\S]*?<\/noscript>/gi, " ").replace(/<textarea[\s\S]*?<\/textarea>/gi, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<\/(p|div|li|h[1-6]|tr|section|article|header|footer|nav)>/gi, "\n").replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, " ");
@@ -1195,7 +1244,7 @@ function makeWebSearchTool(options = {}) {
1195
1244
  if (provider === "anthropic") {
1196
1245
  const akey = options.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY;
1197
1246
  if (!akey) return "Error: WebSearchAnthropic requires ANTHROPIC_API_KEY (set in env)";
1198
- return await anthropicSearch(q, { key: akey, model: options.model ?? "claude-haiku-4-5", fetch: doFetch, signal: ctl.signal });
1247
+ return await anthropicSearch(q, { key: akey, model: options.model ?? latestClaude("haiku").slice("anthropic/".length), fetch: doFetch, signal: ctl.signal });
1199
1248
  }
1200
1249
  if (useFirecrawl) {
1201
1250
  if (!fcKey) return "Error: Firecrawl provider selected but FIRECRAWL_API_KEY is not set";
@@ -1237,6 +1286,7 @@ var init_tools_web = __esm({
1237
1286
  "src/tools.web.ts"() {
1238
1287
  "use strict";
1239
1288
  init_logging();
1289
+ init_models();
1240
1290
  log4 = forComponent("web");
1241
1291
  webFetchTool = makeWebFetchTool();
1242
1292
  webSearchTool = makeWebSearchTool();
@@ -3729,57 +3779,10 @@ ${sections.join("\n\n---\n\n")}`;
3729
3779
 
3730
3780
  // src/subagent.ts
3731
3781
  init_tools();
3782
+ init_models();
3732
3783
  import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
3733
3784
  import { join as join3 } from "path";
3734
3785
 
3735
- // src/models.ts
3736
- var MODEL_ALIASES = {
3737
- fable: "claude-fable-5",
3738
- "fable-5": "claude-fable-5",
3739
- opus: "claude-opus-5-5",
3740
- "opus-5-5": "claude-opus-5-5",
3741
- "opus-5": "claude-opus-5",
3742
- "opus-4-8": "claude-opus-4-8",
3743
- "opus-4-7": "claude-opus-4-7",
3744
- "opus-4-6": "claude-opus-4-6",
3745
- sonnet: "claude-sonnet-4-6",
3746
- haiku: "claude-haiku-4-5-20251001"
3747
- };
3748
- function resolveModelAlias(input) {
3749
- const slash = input.indexOf("/");
3750
- const prefix = slash === -1 ? "" : input.slice(0, slash + 1);
3751
- const rest = slash === -1 ? input : input.slice(slash + 1);
3752
- if (prefix === "claude-code/") return input;
3753
- return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);
3754
- }
3755
- function modelProvider(model) {
3756
- const i = model.indexOf("/");
3757
- if (i !== -1) return model.slice(0, i);
3758
- if (/^claude-/i.test(model)) return "anthropic";
3759
- return `?${model}`;
3760
- }
3761
- function modelShortLabel(model) {
3762
- if (!model) return "default";
3763
- const id = model.split("/").pop();
3764
- return id.replace(/^claude-/, "").replace(/-\d.*$/, "");
3765
- }
3766
- function resolveModelSwitch(opts) {
3767
- const { requested, current, prior, allowed } = opts;
3768
- if (!requested) return { model: current, denied: false };
3769
- if (allowed && !allowed.some((a) => modelShortLabel(a) === modelShortLabel(requested))) {
3770
- return { model: current, denied: true };
3771
- }
3772
- const fmt = opts.format ?? ((from, to) => `_[switching from ${from} to ${to}]_
3773
-
3774
- `);
3775
- const changed = prior !== void 0 && modelShortLabel(prior) !== modelShortLabel(requested);
3776
- return {
3777
- model: requested,
3778
- denied: false,
3779
- notice: changed ? fmt(modelShortLabel(prior), modelShortLabel(requested)) : void 0
3780
- };
3781
- }
3782
-
3783
3786
  // src/permissions.ts
3784
3787
  init_tools_structured();
3785
3788
  function commandMatches(glob, cmd) {
@@ -4417,6 +4420,9 @@ ${r.error ? `ERROR: ${r.error}` : r.text || "(no summary)"}${extra}`);
4417
4420
  };
4418
4421
  }
4419
4422
 
4423
+ // src/Agent.ts
4424
+ init_models();
4425
+
4420
4426
  // src/agents.ts
4421
4427
  function parseAgentFrontmatter(md) {
4422
4428
  const m = md.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
@@ -4552,7 +4558,7 @@ var AgentOptions = class {
4552
4558
  * browser build (which always passes its own fs) never pulls in `node:fs`. Pass `fs` explicitly to
4553
4559
  * use any other backend (and to keep full control of the jail). */
4554
4560
  fs;
4555
- model = "anthropic/claude-sonnet-4-6";
4561
+ model = latestClaude("sonnet");
4556
4562
  systemPrompt = "You are a coding agent operating on a virtual filesystem. Use the `bash` tool to explore (ls, grep, find, cat) and the `Read`/`Edit` tools to view and modify files. Always Read a file before Editing it \u2014 and before overwriting it with `Write` or a whole-file `ApplyEdits`: Write replaces the ENTIRE file, so blindly writing over an existing one destroys its unseen contents. If a file already exists, Read it first, then merge your changes (or Edit the specific part). When multiple tool calls are independent (e.g. several Reads or Greps with no dependency between them), issue them together in a single turn rather than one at a time. When the task is complete, reply with a short summary and make no further tool calls.\nRecovery: every turn's file changes are checkpointed. If the user asks to undo/revert/recover edits (including a file you overwrote), tell them to run `/rewind` (or `/undo` for the last turn) to restore the working tree \u2014 checkpoints cover files you edited even in .gitignored dirs. You cannot run `/rewind` yourself; surface it as the recovery path.\nWhen working with a knowledge base or document workspace: use `RepoMap` with scope \"docs\" to see all document headings and summaries (orient first); use `MemorySearch` to find memories by content when you don't know the exact slug; use `Recall` with multiple slugs to batch-load related facts in one call. Prefer targeted loads over reading everything \u2014 orient \u2192 search \u2192 load \u2192 act.";
4557
4563
  tools = defaultTools();
4558
4564
  /** claude-code/* only: which agentx tools reach it as the in-process `agentx` MCP server (ccBridge.ts). 'memory' =
@@ -5987,6 +5993,7 @@ function toolKey(name) {
5987
5993
  }
5988
5994
 
5989
5995
  // src/index.ts
5996
+ init_models();
5990
5997
  init_NodeDiskFilesystem();
5991
5998
 
5992
5999
  // src/BodDbFilesystem.ts
@@ -6800,6 +6807,7 @@ function digestRun(messages, maxChars) {
6800
6807
  import { MemFilesystem as MemFilesystem2 } from "@livx.cc/wcli/core";
6801
6808
  init_llm();
6802
6809
  init_logging();
6810
+ init_models();
6803
6811
 
6804
6812
  // node_modules/@bod.ee/voice/src/core/logging.ts
6805
6813
  function consoleForComponent(name) {
@@ -9379,9 +9387,9 @@ var DuplexAgentOptions = class {
9379
9387
  // mislabels gpt-oss harmony channels under load, leaking raw analysis into the spoken `final` channel
9380
9388
  // (and misfiring Hold). 120b is the same price tier (~$0.15/$0.60) — the quality/cost trade is free.
9381
9389
  reflexModel = "groq/openai/gpt-oss-120b";
9382
- actModel = "anthropic/claude-sonnet-4-6";
9390
+ actModel = latestClaude("sonnet");
9383
9391
  /** Premium reasoning model. Set to `false` to disable the Think tier entirely. */
9384
- thinkModel = "anthropic/claude-opus-4-8";
9392
+ thinkModel = latestClaude("opus");
9385
9393
  /** Per-worker providerOptions, derived from the worker's actual model at spawn time (IoC — keeps duplex
9386
9394
  * provider-agnostic). Workers override the reflex/main model, so provider-specific options (e.g. cursor's
9387
9395
  * cwd/cursorSession) must be recomputed for the worker's model, never inherited from the main template —
@@ -10959,11 +10967,12 @@ Another agent just implemented the above. Independently check the CURRENT state
10959
10967
  // src/modelRouter.ts
10960
10968
  init_llm();
10961
10969
  init_logging();
10970
+ init_models();
10962
10971
  var log23 = forComponent("ModelRouter");
10963
10972
  var ModelRouterOptions = class {
10964
10973
  classifier = jevClassifier();
10965
10974
  /** Capability tiers → model id (any provider). basic/standard run the main session; premium is only ever an offload. */
10966
- models = { basic: "anthropic/claude-haiku-4-5-20251001", standard: "anthropic/claude-sonnet-4-6", premium: "anthropic/claude-opus-5-5" };
10975
+ models = { basic: latestClaude("haiku"), standard: latestClaude("sonnet"), premium: latestClaude("opus") };
10967
10976
  /** Escalate to a stronger tier when it holds at least this much probability mass (upward bias). */
10968
10977
  threshold = 0.25;
10969
10978
  /** P(same thread) at or above which the previous premium side-conversation is resumed. */
@@ -11531,6 +11540,7 @@ export {
11531
11540
  imageRefResult,
11532
11541
  jevAvailable,
11533
11542
  jevClassifier,
11543
+ latestClaude,
11534
11544
  lessonCapture,
11535
11545
  loadAgents,
11536
11546
  loadCommands,