ruvnet-brain 4.5.3 β†’ 4.5.5

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.
Files changed (56) hide show
  1. package/README.md +2 -2
  2. package/bin/install.mjs +148 -31
  3. package/config/model-router/catalog.template.json +126 -52
  4. package/config/model-router/policy.default.mjs +94 -75
  5. package/config/model-router/qualification-contract.json +124 -0
  6. package/config/model-router/routing-eval-cases.json +275 -0
  7. package/config/model-router/routing-policy.template.json +76 -0
  8. package/config/model-router/weekly-analyst-instruction.md +60 -0
  9. package/data/model-catalog.json +44 -49
  10. package/package.json +3 -1
  11. package/plugin/.claude-plugin/plugin.json +1 -1
  12. package/plugin/.codex-plugin/plugin.json +1 -1
  13. package/plugin/hooks/codex-hooks.json +40 -3
  14. package/plugin/hooks/hook-contracts.json +218 -19
  15. package/plugin/hooks/hooks.json +51 -2
  16. package/plugin/scripts/agentdb-recall.mjs +101 -30
  17. package/plugin/scripts/codex-hook-adapter.mjs +18 -9
  18. package/plugin/scripts/continuity-hook-policy.mjs +9 -0
  19. package/plugin/scripts/continuity-journal.mjs +33 -33
  20. package/plugin/scripts/ground-ruvnet.sh +5 -5
  21. package/plugin/scripts/hook-shim.mjs +4 -30
  22. package/plugin/scripts/project-capture-queue.mjs +333 -0
  23. package/plugin/scripts/project-progression-contract.mjs +1 -1
  24. package/plugin/scripts/project-progression-hook.mjs +3 -3
  25. package/plugin/scripts/project-progression-producer.mjs +53 -28
  26. package/plugin/scripts/project-progression-session-start.mjs +44 -4
  27. package/plugin/scripts/project-progression-store.mjs +14 -0
  28. package/plugin/scripts/project-transition-hook.mjs +204 -0
  29. package/plugin/scripts/session-snapshot-hook.mjs +44 -272
  30. package/plugin/scripts/session-start-budget.mjs +2 -2
  31. package/plugin/scripts/turn-outcome-capture.mjs +125 -47
  32. package/plugin/scripts/turn-transport-journal.mjs +106 -0
  33. package/scripts/codex-hook-trust-reconcile.mjs +247 -0
  34. package/scripts/codex-routed.sh +3 -36
  35. package/scripts/goldie-weekly.sh +8 -64
  36. package/scripts/metaharness-router.mjs +7 -1
  37. package/scripts/model-analyst-sandbox.mjs +54 -0
  38. package/scripts/model-currency-evidence.mjs +139 -0
  39. package/scripts/model-currency.mjs +230 -0
  40. package/scripts/model-native-catalog.mjs +111 -0
  41. package/scripts/model-native-qualification.mjs +251 -0
  42. package/scripts/model-router-agent-hook.mjs +136 -0
  43. package/scripts/model-router-dispatch.mjs +161 -0
  44. package/scripts/model-router-engine.mjs +155 -104
  45. package/scripts/model-routing-eval.mjs +108 -0
  46. package/scripts/model-routing-gateway.mjs +420 -0
  47. package/scripts/model-routing-launchers.mjs +174 -0
  48. package/scripts/model-routing-policy-promotion.mjs +203 -0
  49. package/scripts/model-weekly-analyst.mjs +299 -0
  50. package/scripts/model-weekly-assessment.mjs +91 -0
  51. package/scripts/model-weekly-cycle.mjs +183 -0
  52. package/scripts/model-weekly-qualification.mjs +362 -0
  53. package/scripts/native-subscription-usage.mjs +57 -0
  54. package/scripts/release-qualification-contract.mjs +54 -0
  55. package/scripts/security-guidance-codex-compat.mjs +142 -0
  56. package/scripts/user-model-prompt-hook.mjs +69 -0
package/README.md CHANGED
@@ -7,7 +7,7 @@ Created: 2026-06-29 22:36:38 EDT
7
7
 
8
8
  # 🧠 RuvNet Brain
9
9
 
10
- ### 🧠 RuvNet Brain β€” [![RuvNet Brain version 4.5.3 β€” updated 2026-07-30 03:24 EDT](https://img.shields.io/badge/version_4.5.3-updated_2026--07--30_03:24_EDT-1E90FF?style=for-the-badge&labelColor=0757BA)](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
10
+ ### 🧠 RuvNet Brain β€” [![RuvNet Brain version 4.5.5 β€” updated 2026-07-30 03:24 EDT](https://img.shields.io/badge/version_4.5.5-updated_2026--07--30_03:24_EDT-1E90FF?style=for-the-badge&labelColor=0757BA)](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
11
11
 
12
12
  **A portable, source-grounded brain over Reuven Cohen's (rUv's) RuvNet stack β€” delivered as a Claude Code plugin that makes Claude _use_ the stack instead of fighting it.**
13
13
 
@@ -562,7 +562,7 @@ node forge-ask-all.mjs --dir . --q "How does RuVector implement HNSW vector sear
562
562
 
563
563
  This project versions in the open (see the live badge up top for the exact plugin version; the downloadable knowledge bundle is a separate track) β€” we don't claim β€œdone,” β€œcomplete,” or β€œzero hallucinations.” Where it stands:
564
564
 
565
- - βœ… **The grounding brain is real and proven** β€” 199 public stores Β· 162,439 public source chunks, dual embeddings, cross-encoder rerank, plugin (MCP tool + explicit skills; automatic hooks retired), all re-runnable.
565
+ - βœ… **The grounding brain is real and proven** β€” 199 public stores Β· 162,680 public source chunks, dual embeddings, cross-encoder rerank, plugin (MCP tool + explicit skills; automatic hooks retired), all re-runnable.
566
566
  - βœ… **Code-level depth** β€” the code-rich repos are indexed to full function bodies; β€œhow is it implemented?” returns the implementation. Verified in the shipped bundle (clean-room 3/3).
567
567
  - βœ… **Routing holds** β€” named 47/48, described 26/28, scenario 7/8; behavioral L1–L3 all pass (**L4 downgraded β€” it measures that the brain spoke, not that anything listened**); private stores fenced out of the public bundle (zero-leak verified).
568
568
  - ⚠️ **Two routing residuals** (above) β€” surfaced, not hidden.
package/bin/install.mjs CHANGED
@@ -79,6 +79,8 @@ import {
79
79
  } from '../scripts/console-runtime-identity.mjs';
80
80
  import { shellDiff as pluginShellDiff } from '../plugin/scripts/host-shell-boundary.mjs';
81
81
  import { codexTrustChanges, CODEX_TRUST_ACTION } from '../scripts/codex-hook-trust.mjs';
82
+ import { repairSecurityGuidance } from '../scripts/security-guidance-codex-compat.mjs';
83
+ import { installNativeLaunchers } from '../scripts/model-routing-launchers.mjs';
82
84
  import { readConsoleReceipts, replaceStaleConsoles } from '../scripts/console-instances.mjs';
83
85
  import { moveBrain, MoveRefused } from '../scripts/move-brain.mjs';
84
86
  import { brainLocation } from '../plugin/scripts/brain-location.mjs';
@@ -2014,6 +2016,32 @@ export function codexPluginStatus(options = {}) {
2014
2016
  };
2015
2017
  }
2016
2018
 
2019
+ // User-scoped standing consent applies across projects, never implicitly to other installations.
2020
+ export function repairReleasedCodexHookTrust(status, { codexHome = codexHomeDir(), codexBin = process.env.CODEX_BIN || 'codex', cwd = process.cwd(), run = spawnSync } = {}) {
2021
+ if (TEST_MODE || !status?.installed || !status?.enabled) return { state: 'not-applicable', changed: false };
2022
+ let profile;
2023
+ try { profile = JSON.parse(fs.readFileSync(path.join(os.homedir(), '.claude', 'model-router', 'profile.json'), 'utf8')); } catch { return { state: 'not-authorized', changed: false }; }
2024
+ if (profile.automaticHookTrustUpdates !== true) return { state: 'not-authorized', changed: false };
2025
+ let compatibility;
2026
+ try { compatibility = repairSecurityGuidance({ codexHome, apply: true }); }
2027
+ catch (error) { compatibility = { status: 'blocked', reason: error.message }; }
2028
+ const installedRoot = codexInstalledPluginRoot({ codexHome, status });
2029
+ if (!installedRoot) return { state: 'blocked', changed: false, reason: 'Installed plugin root unavailable' };
2030
+ const helper = path.join(REPO_ROOT, 'scripts', 'codex-hook-trust-reconcile.mjs');
2031
+ const result = run(process.execPath, [helper, '--reconcile-installed'], {
2032
+ input: JSON.stringify({ installedVersion: status.version, hooksPath: path.join(installedRoot, 'hooks', 'codex-hooks.json'),
2033
+ configPath: path.join(codexHome, 'config.toml'), nativeBinary: codexBin, cwd }),
2034
+ encoding: 'utf8', timeout: 30000, maxBuffer: 128 * 1024, env: process.env,
2035
+ });
2036
+ try {
2037
+ const receipt = JSON.parse(result.stdout);
2038
+ if (!result.error && ['registry-verified', 'unchanged', 'blocked', 'degraded'].includes(receipt.state)) return { ...receipt, compatibility };
2039
+ } catch { /* a command exit alone never establishes hook trust */ }
2040
+ return result.error?.code === 'ENOENT'
2041
+ ? { state: 'blocked', changed: false, reason: 'Verified hook trust helper could not start' }
2042
+ : { state: 'degraded', changed: 'unknown', reason: 'Hook trust helper acknowledgement unavailable; inspect native registry before retrying' };
2043
+ }
2044
+
2017
2045
  export function wireCodexPlugin({
2018
2046
  codexDir = codexHomeDir(),
2019
2047
  codexHome = codexDir,
@@ -2062,9 +2090,13 @@ export function wireCodexPlugin({
2062
2090
  const hooksNeedingReview = codexHooksNeedingReview(before.installed
2063
2091
  ? codexInstalledPluginRoot({ codexHome, status: before }) : null, path.join(REPO_ROOT, 'plugin'));
2064
2092
  if (before.installed && before.enabled && versionSatisfies(before.version, expectedVersion)) {
2065
- if (announce) ok(`Codex Brain plugin already installed and enabled (${before.version || 'version unknown'}) β€” no changes.`);
2093
+ const hookTrust = runJson === runCodexJson ? repairReleasedCodexHookTrust(before, options) : null;
2094
+ if (announce) {
2095
+ ok(`Codex Brain plugin already installed and enabled (${before.version || 'version unknown'}).`);
2096
+ if (hookTrust && ['blocked', 'degraded'].includes(hookTrust.state)) warn(`Automatic Brain hook trust repair ${hookTrust.state}: ${hookTrust.reason}`);
2097
+ }
2066
2098
  return {
2067
- host: true, action: 'unchanged', ...before,
2099
+ host: true, action: 'unchanged', ...before, ...(hookTrust ? { hookTrust } : {}),
2068
2100
  shellChanged: shellBoundary.changed, shellChangedPaths: shellBoundary.paths,
2069
2101
  restartRequired: false,
2070
2102
  };
@@ -2107,14 +2139,17 @@ export function wireCodexPlugin({
2107
2139
  if (announce) warn('Codex accepted the install command but the Brain plugin is not installed and enabled.');
2108
2140
  return { host: true, action: 'verification-failed', expectedVersion, ...after };
2109
2141
  }
2142
+ const hookTrust = runJson === runCodexJson ? repairReleasedCodexHookTrust(after, options) : null;
2143
+ const trustVerified = hookTrust && ['registry-verified', 'unchanged'].includes(hookTrust.state);
2110
2144
  if (announce) {
2111
2145
  ok(`Codex Brain plugin installed and enabled (${after.version || 'version unknown'}).`);
2146
+ if (hookTrust && ['blocked', 'degraded'].includes(hookTrust.state)) warn(`Automatic Brain hook trust repair ${hookTrust.state}: ${hookTrust.reason}`);
2112
2147
  if (shellBoundary.restartRequired) {
2113
2148
  warn(`boot-level plugin declarations changed; restart Codex, then review them in /hooks (${shellBoundary.paths.join(', ') || shellBoundary.reason}).`);
2114
2149
  } else if (before.installed && before.version !== after.version) {
2115
2150
  info(' body-only update: the Stable Spine is live on the next hook/MCP call; no restart is required.');
2116
2151
  }
2117
- if (hooksNeedingReview.length) {
2152
+ if (hooksNeedingReview.length && !trustVerified) {
2118
2153
  warn(`Codex will NOT run ${hooksNeedingReview.length} Brain hook${hooksNeedingReview.length === 1 ? '' : 's'} until you review`
2119
2154
  + ` ${hooksNeedingReview.length === 1 ? 'it' : 'them'} (${hooksNeedingReview.map((h) => `${h.key.split(':').slice(-3).join(':')} ${h.status}`).join(', ')}).`);
2120
2155
  info(` ${CODEX_TRUST_ACTION}`);
@@ -2123,7 +2158,8 @@ export function wireCodexPlugin({
2123
2158
  return {
2124
2159
  host: true,
2125
2160
  action: before.installed ? 'updated' : 'installed',
2126
- hooksNeedingReview,
2161
+ hooksNeedingReview: trustVerified ? [] : hooksNeedingReview,
2162
+ ...(hookTrust ? { hookTrust } : {}),
2127
2163
  ...after,
2128
2164
  shellChanged: shellBoundary.changed,
2129
2165
  shellChangedPaths: shellBoundary.paths,
@@ -2443,11 +2479,11 @@ export function automaticHookRetirementStatus(root = REPO_ROOT, { scope = 'sourc
2443
2479
  if (pairs.length !== expectedPairs.length || expectedPairs.some((pair) => !pairs.includes(pair))) {
2444
2480
  errors.push(`${contractsFile}: contracts must list exactly the continuity handlers (${expectedPairs.join(', ')})`);
2445
2481
  }
2446
- // One allowlist entry per DISTINCT (event, matcher): the matcher is a property of the event, so
2447
- // three snapshot registrations on three events need three entries, not three copies of one.
2448
- const expectedMatchers = [...new Set(continuityRegistrations().map((spec) => `${spec.event}:${spec.matcher}`))];
2482
+ // Matchers belong to a host registry: one host's exception grants no authority to another.
2483
+ const expectedMatchers = [['plugin', 'claude'], ['codex', 'codex']].flatMap(([layer, host]) =>
2484
+ [...new Set(continuityRegistrations(host).map((spec) => `${layer}:${spec.event}:${spec.matcher}`))]);
2449
2485
  const declaredMatchers = Array.isArray(contracts.matcherAllowlist)
2450
- ? contracts.matcherAllowlist.map((row) => `${row?.event}:${row?.matcher}`) : [];
2486
+ ? contracts.matcherAllowlist.map((row) => `${row?.layer}:${row?.event}:${row?.matcher}`) : [];
2451
2487
  if (declaredMatchers.length !== expectedMatchers.length
2452
2488
  || expectedMatchers.some((entry) => !declaredMatchers.includes(entry))) {
2453
2489
  errors.push(`${contractsFile}: matcherAllowlist must list exactly the continuity matchers (${expectedMatchers.join(', ')})`);
@@ -3934,6 +3970,7 @@ async function runUpdate() {
3934
3970
  } catch (error) {
3935
3971
  warn(`managed model additions were not merged (${error.message}); your catalog was left unchanged`);
3936
3972
  }
3973
+ refreshManagedRouterDefault();
3937
3974
  process.exitCode = 0;
3938
3975
  return;
3939
3976
  }
@@ -4207,6 +4244,8 @@ async function runUpdate() {
4207
4244
  // updated. Additive and non-interactive; their overrides win, metered rows are never auto-enabled,
4208
4245
  // and a failure here is reported but never fails an otherwise-good update.
4209
4246
  if (updateStatus === 0) {
4247
+ const defaultReceipt = refreshManagedRouterDefault();
4248
+ recordRefreshAdvisory(refreshReceipt, 'managed-router-default', defaultReceipt.action === 'failed' ? 'SKIP' : 'PASS', defaultReceipt);
4210
4249
  try {
4211
4250
  const managed = applyManagedCatalogUpdate({
4212
4251
  routerDir: path.join(os.homedir(), '.claude', 'model-router'),
@@ -4849,30 +4888,110 @@ export function parseNightlyAnswer(answer) {
4849
4888
  // suppression flags) is testable in-process under RUVNET_BRAIN_IMPORT_ONLY=1 without a real install.
4850
4889
  // Returns a status string; never throws (the caller also guards β€” a finished install must never
4851
4890
  // be broken by an optional offer).
4891
+ // The default classifier is managed executable source; overrides belong in policy.mjs.
4892
+ // Preserve every prior default by its content digest before replacing it atomically.
4893
+ export function syncManagedRouterDefault({ routerDir = path.join(os.homedir(), '.claude', 'model-router'), packageRoot = REPO_ROOT } = {}) {
4894
+ const source = fs.readFileSync(path.join(packageRoot, 'config', 'model-router', 'policy.default.mjs'));
4895
+ const destination = path.join(routerDir, 'policy.default.mjs');
4896
+ fs.mkdirSync(routerDir, { recursive: true });
4897
+ const prior = fs.existsSync(destination) ? fs.readFileSync(destination) : null;
4898
+ if (prior?.equals(source)) return { action: 'unchanged' };
4899
+ let backup = null;
4900
+ if (prior) {
4901
+ backup = `${destination}.pre-managed-upgrade-${crypto.createHash('sha256').update(prior).digest('hex').slice(0, 16)}`;
4902
+ try { fs.writeFileSync(backup, prior, { flag: 'wx', mode: 0o600 }); }
4903
+ catch (error) {
4904
+ if (error.code !== 'EEXIST' || !fs.readFileSync(backup).equals(prior)) throw error;
4905
+ }
4906
+ }
4907
+ const temporary = `${destination}.tmp-${process.pid}-${crypto.randomUUID()}`;
4908
+ try {
4909
+ fs.writeFileSync(temporary, source, { flag: 'wx', mode: prior ? fs.statSync(destination).mode & 0o777 : 0o600 });
4910
+ fs.renameSync(temporary, destination);
4911
+ } finally { fs.rmSync(temporary, { force: true }); }
4912
+ return { action: prior ? 'upgraded' : 'created', backup };
4913
+ }
4914
+
4915
+ export function refreshInstalledModelLaunchers({ sourceRoot = REPO_ROOT, home = os.homedir() } = {}) {
4916
+ const file = path.join(home, '.cache', 'ruvnet-brain', 'model-routing', 'launcher-config.json');
4917
+ if (!fs.existsSync(file)) return { action: 'not-installed' };
4918
+ const previous = JSON.parse(fs.readFileSync(file, 'utf8'));
4919
+ const receipt = installNativeLaunchers({ sourceRoot, home, extensionsRoot: previous.extensionsRoot, nodeBinary: previous.nodeBinary, apply: true });
4920
+ return { action: receipt.config.runtimeDigest === previous.runtimeDigest ? 'unchanged' : 'updated', runtimeDigest: receipt.config.runtimeDigest };
4921
+ }
4922
+
4923
+ export function syncManagedRouterTools({ routerDir = path.join(os.homedir(), '.claude', 'model-router'), packageRoot = REPO_ROOT } = {}) {
4924
+ const destination = path.join(routerDir, 'bin');
4925
+ fs.mkdirSync(destination, { recursive: true });
4926
+ let copied = 0;
4927
+ for (const name of ['model-router-engine.mjs', 'model-router-setup.mjs', 'model-router-status.mjs', 'model-router-outcome.mjs', 'subscription-hosts.mjs', 'review-model-defaults.mjs', 'dual-host-deliberation.mjs', 'dual-host-suggest.mjs', 'route-cheap.mjs', 'dispatch-receipt.mjs', 'metaharness-receipts.mjs', 'metaharness-router.mjs', 'model-router-dispatch.mjs', 'model-currency.mjs', 'model-currency-evidence.mjs', 'user-model-prompt-hook.mjs', 'model-router-agent-hook.mjs', 'model-weekly-assessment.mjs', 'model-weekly-analyst.mjs', 'model-weekly-cycle.mjs', 'model-weekly-qualification.mjs', 'model-native-qualification.mjs', 'model-routing-launchers.mjs', 'model-native-catalog.mjs', 'model-analyst-sandbox.mjs', 'model-routing-policy-promotion.mjs', 'model-routing-gateway.mjs', 'native-subscription-usage.mjs', 'codex-routed.sh', 'goldie-weekly.sh']) {
4928
+ const source = path.join(packageRoot, 'scripts', name);
4929
+ if (!fs.existsSync(source)) throw new Error(`Packaged router tool missing: ${name}`);
4930
+ const target = path.join(destination, name);
4931
+ const bytes = fs.readFileSync(source);
4932
+ if (fs.existsSync(target) && fs.readFileSync(target).equals(bytes)) continue;
4933
+ const temporary = `${target}.${crypto.randomUUID()}.tmp`;
4934
+ try { fs.writeFileSync(temporary, bytes, { mode: name.endsWith('.sh') ? 0o755 : 0o600 }); fs.renameSync(temporary, target); }
4935
+ finally { fs.rmSync(temporary, { force: true }); }
4936
+ copied++;
4937
+ }
4938
+ // Preserve the package-relative import used by the optional legacy routing helper.
4939
+ const runtimeRelative = path.join('plugin', 'scripts', 'runtime-preferences.mjs');
4940
+ const runtimeTarget = path.join(routerDir, runtimeRelative);
4941
+ const runtimeBytes = fs.readFileSync(path.join(packageRoot, runtimeRelative));
4942
+ if (!fs.existsSync(runtimeTarget) || !fs.readFileSync(runtimeTarget).equals(runtimeBytes)) {
4943
+ fs.mkdirSync(path.dirname(runtimeTarget), { recursive: true });
4944
+ const temporary = `${runtimeTarget}.${crypto.randomUUID()}.tmp`;
4945
+ try { fs.writeFileSync(temporary, runtimeBytes, { mode: 0o600 }); fs.renameSync(temporary, runtimeTarget); }
4946
+ finally { fs.rmSync(temporary, { force: true }); }
4947
+ copied++;
4948
+ }
4949
+ const qualificationSource = path.join(packageRoot, 'config', 'model-router', 'qualification-contract.json');
4950
+ const qualificationTarget = path.join(routerDir, 'qualification-contract.json');
4951
+ const qualificationBytes = fs.readFileSync(qualificationSource);
4952
+ if (!fs.existsSync(qualificationTarget) || !fs.readFileSync(qualificationTarget).equals(qualificationBytes)) {
4953
+ const temporary = `${qualificationTarget}.${crypto.randomUUID()}.tmp`;
4954
+ try { fs.writeFileSync(temporary, qualificationBytes, { mode: 0o600 }); fs.renameSync(temporary, qualificationTarget); }
4955
+ finally { fs.rmSync(temporary, { force: true }); }
4956
+ copied++;
4957
+ }
4958
+ const launchers = routerDir === path.join(os.homedir(), '.claude', 'model-router')
4959
+ ? refreshInstalledModelLaunchers({ sourceRoot: packageRoot }) : { action: 'custom-router-directory' };
4960
+ return { action: copied ? 'updated' : 'unchanged', copied, launchers };
4961
+ }
4962
+
4963
+ function refreshManagedRouterDefault() {
4964
+ try {
4965
+ const receipt = syncManagedRouterDefault();
4966
+ syncManagedRouterTools();
4967
+ if (receipt.action === 'created') ok('installed managed model-router default (custom overrides belong in policy.mjs)');
4968
+ if (receipt.action === 'upgraded') ok(`upgraded managed model-router default; prior source preserved at ${receipt.backup}`);
4969
+ return receipt;
4970
+ } catch (error) {
4971
+ warn(`managed model-router default was not refreshed (${error.message}); native dispatch remains fail-closed`);
4972
+ return { action: 'failed' };
4973
+ }
4974
+ }
4975
+
4852
4976
  // ── MetaHarness router: config materialization + THIS user's subscription profile (2026-07-12) ──
4853
4977
  // Stuart's mandate: subscription-awareness must be per-user. Detect what the machine can PROVE
4854
4978
  // (Codex auth mode from ~/.codex/auth.json's SHAPE β€” never its secrets), ASK what it can't (Claude
4855
4979
  // plan tiers aren't probeable from disk), and RECORD both with their basis, so the router's
4856
4980
  // $0-floor never assumes a plan this user doesn't have (billing them) or misses one they do
4857
4981
  // (wasting it). Config templates ship in the npm package's config/; router tools are copied to
4858
- // ~/.claude/model-router/bin/ because the npx run dir vanishes after install. Never overwrites
4859
- // user-edited files. Non-fatal like every offer.
4982
+ // ~/.claude/model-router/bin/ because the npx run dir vanishes after install. Managed defaults
4983
+ // upgrade with a backup; custom policy.mjs, profiles and reviewed allocation overrides remain user-owned. Non-fatal like every offer.
4860
4984
  export async function offerRouterProfile() {
4861
4985
  if (TEST_MODE) return 'suppressed';
4862
4986
  const routerDir = path.join(os.homedir(), '.claude', 'model-router');
4863
4987
  const pkgRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
4864
4988
  step(
4865
- 'MetaHarness model router β€” the right model for each task, cheapest first',
4866
- "your subscription models are $0 marginal; the router just needs to know which ones YOU have",
4989
+ 'Model router β€” appropriate intelligence for each task',
4990
+ "native subscription routes preserve your overrides; weekly analysis checks the evidence",
4867
4991
  );
4868
4992
 
4869
4993
  fs.mkdirSync(path.join(routerDir, 'bin'), { recursive: true });
4870
- const policySrc = path.join(pkgRoot, 'config', 'model-router', 'policy.default.mjs');
4871
- const policyDst = path.join(routerDir, 'policy.default.mjs');
4872
- if (fs.existsSync(policySrc) && !fs.existsSync(policyDst)) {
4873
- fs.copyFileSync(policySrc, policyDst);
4874
- ok('installed policy.default.mjs (edit freely β€” goldie keeps prices fresh where scheduled)');
4875
- }
4994
+ refreshManagedRouterDefault();
4876
4995
  // ONE implementation of the managed-catalog step, shared with runUpdate() (issue #87). It used to
4877
4996
  // live here only, which is exactly why existing users never received managed additions.
4878
4997
  try {
@@ -4882,19 +5001,17 @@ export async function offerRouterProfile() {
4882
5001
  } catch (error) {
4883
5002
  warn(`managed model additions were not merged (${error.message}); your existing catalog was left unchanged`);
4884
5003
  }
4885
- let copied = 0;
4886
- // dispatch-receipt + metaharness-receipts added 2026-07-13: without the LOGGER, subagent routing is
4887
- // invisible; without the VIEWER, the user has no scoreboard to hold it to. Shipping one without the
4888
- // other is how a router ends up "working" with three test pings in its log and nobody the wiser.
4889
- // (dispatch-receipt.mjs relative-imports route-cheap.mjs β€” they land in the same bin/ dir, so it resolves.)
4890
- for (const t of ['model-router-engine.mjs', 'model-router-setup.mjs', 'model-router-status.mjs', 'model-router-outcome.mjs', 'subscription-hosts.mjs', 'dual-host-deliberation.mjs', 'dual-host-suggest.mjs', 'route-cheap.mjs', 'dispatch-receipt.mjs', 'metaharness-receipts.mjs', 'codex-routed.sh']) {
4891
- const s = path.join(pkgRoot, 'scripts', t);
4892
- if (fs.existsSync(s)) { fs.copyFileSync(s, path.join(routerDir, 'bin', t)); copied++; }
4893
- }
4894
- if (copied) {
4895
- try { fs.chmodSync(path.join(routerDir, 'bin', 'codex-routed.sh'), 0o755); } catch { /* not fatal */ }
4896
- ok(`${copied} router tools at ~/.claude/model-router/bin/ (stable path β€” the npx dir vanishes)`);
4897
- }
5004
+ const toolsReceipt = syncManagedRouterTools({ routerDir, packageRoot: pkgRoot });
5005
+ if (toolsReceipt.copied) ok(`updated ${toolsReceipt.copied} managed routing tools`);
5006
+
5007
+ // Allocation is user-owned. A shipped seed retains its real review date; installation never
5008
+ // certifies it fresh or overwrites a reviewed user policy.
5009
+ const selectionSrc = path.join(pkgRoot, 'config', 'model-router', 'routing-policy.template.json');
5010
+ const selectionDst = path.join(routerDir, 'routing-policy.json');
5011
+ if (fs.existsSync(selectionSrc) && !fs.existsSync(selectionDst)) fs.copyFileSync(selectionSrc, selectionDst);
5012
+ const instructionSrc = path.join(pkgRoot, 'config', 'model-router', 'weekly-analyst-instruction.md');
5013
+ const instructionDst = path.join(routerDir, 'weekly-analyst-instruction.md');
5014
+ if (fs.existsSync(instructionSrc) && !fs.existsSync(instructionDst)) fs.copyFileSync(instructionSrc, instructionDst);
4898
5015
 
4899
5016
  const profilePath = path.join(routerDir, 'profile.json');
4900
5017
  if (fs.existsSync(profilePath)) { ok('subscription profile already exists β€” routing already uses it'); return 'already'; }
@@ -1,78 +1,60 @@
1
1
  {
2
2
  "_comment": "Model candidate catalog for model-router-engine.mjs. EDIT ME freely. Pricing is $/Mtok. 'verified' names the source+date a price was confirmed live; null pricing means UNKNOWN \u2014 the engine will NOT invent a cost for it (same honesty rule as route-cheap.mjs). 'harness' = which agent(s) can launch each model \u2014 EMPTY [] means landscape-only: known to exist, but no wired execution path yet, so the engine will never select it (see model-router-engine.mjs pool filter). Do not add a real execution path (an entry to route-cheap.mjs PRICING or a harness value) until dispatch is actually implemented and tested \u2014 an engine that 'chooses' a model it can't run is worse than one that doesn't know the model exists. 'subscription' = harness(es) under which this model is covered by a flat subscription (Claude Max, Codex/ChatGPT), so its marginal cost to the user is ~$0 \u2014 the default policy treats those as free and will NOT prefer a billed model over them (never spend where the subscription is free).",
3
- "managedVersion": 2,
4
- "updated": "2026-08-02 (managed template)",
3
+ "managedVersion": 3,
4
+ "updated": "2026-10-04 (five native subscription launch receipts; Fable remains landscape-only)",
5
5
  "candidates": [
6
6
  {
7
7
  "id": "claude-haiku-4-5-20251001",
8
8
  "provider": "anthropic",
9
- "harness": [
10
- "claude-code"
11
- ],
12
- "subscription": [
13
- "claude-code"
14
- ],
9
+ "harness": [],
10
+ "subscription": [],
15
11
  "tier": "cheap",
16
12
  "costPerMTok": null,
17
13
  "verified": null,
18
- "note": "covered by Claude Max under Claude Code (marginal $0); API price unverified"
14
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
19
15
  },
20
16
  {
21
17
  "id": "claude-sonnet-5",
22
18
  "provider": "anthropic",
23
- "harness": [
24
- "claude-code"
25
- ],
26
- "subscription": [
27
- "claude-code"
28
- ],
19
+ "harness": [],
20
+ "subscription": [],
29
21
  "tier": "mid",
30
22
  "costPerMTok": null,
31
- "verified": null
23
+ "verified": null,
24
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
32
25
  },
33
26
  {
34
27
  "id": "claude-opus-4-8",
35
28
  "provider": "anthropic",
36
- "harness": [
37
- "claude-code"
38
- ],
39
- "subscription": [
40
- "claude-code"
41
- ],
29
+ "harness": [],
30
+ "subscription": [],
42
31
  "tier": "frontier",
43
32
  "costPerMTok": {
44
33
  "in": 5,
45
34
  "out": 25
46
35
  },
47
- "verified": "2026-07-07 route-cheap.mjs FRONTIER (API price; $0 under Max)"
36
+ "verified": "2026-07-07 route-cheap.mjs FRONTIER (API price; $0 under Max)",
37
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
48
38
  },
49
39
  {
50
40
  "id": "claude-opus-5",
51
41
  "provider": "anthropic",
52
- "harness": [
53
- "claude-code"
54
- ],
55
- "subscription": [
56
- "claude-code"
57
- ],
42
+ "harness": [],
43
+ "subscription": [],
58
44
  "tier": "frontier",
59
45
  "costPerMTok": null,
60
46
  "verified": "2026-08-02 Claude Code 2.1.220 subscription launch (canonicalModel claude-opus-5)",
61
- "note": "Launchability is host-scoped. The managed row adds the reviewed candidate; the user's subscription profile still decides whether it is available and covered."
47
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
62
48
  },
63
49
  {
64
50
  "id": "claude-fable-5",
65
51
  "provider": "anthropic",
66
- "harness": [
67
- "claude-code"
68
- ],
69
- "subscription": [
70
- "claude-code"
71
- ],
52
+ "harness": [],
53
+ "subscription": [],
72
54
  "tier": "frontier",
73
55
  "costPerMTok": null,
74
56
  "verified": "2026-07-12 Artificial Analysis leaderboard (WebSearch) \u2014 listed as top-intelligence tier alongside GPT-5.6 Sol",
75
- "note": "covered by Claude Max under Claude Code (marginal $0); API price unverified. Use sparingly per Stuart's directive \u2014 Opus 4.8 measures ~96% of comparable quality at far lower cost; Fable 5 is for tasks that specifically need its top-tier profile, not a default."
57
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
76
58
  },
77
59
  {
78
60
  "id": "deepseek/deepseek-chat",
@@ -123,16 +105,12 @@
123
105
  {
124
106
  "id": "gpt-5.5",
125
107
  "provider": "openai",
126
- "harness": [
127
- "codex"
128
- ],
129
- "subscription": [
130
- "codex"
131
- ],
108
+ "harness": [],
109
+ "subscription": [],
132
110
  "tier": "frontier",
133
111
  "costPerMTok": null,
134
112
  "verified": null,
135
- "note": "Codex-harness model; whether YOUR install can launch it and whether a subscription covers it comes from profile.json (model-router-setup.mjs) and your live ~/.codex/models_cache.json"
113
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
136
114
  },
137
115
  {
138
116
  "id": "gpt-5.6-sol",
@@ -142,7 +120,7 @@
142
120
  "tier": "frontier",
143
121
  "costPerMTok": null,
144
122
  "verified": null,
145
- "note": "Codex-harness model; whether YOUR install can launch it and whether a subscription covers it comes from profile.json (model-router-setup.mjs) and your live ~/.codex/models_cache.json"
123
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
146
124
  },
147
125
  {
148
126
  "id": "gpt-5.6-terra",
@@ -152,7 +130,7 @@
152
130
  "tier": "mid",
153
131
  "costPerMTok": null,
154
132
  "verified": null,
155
- "note": "Codex-harness model; whether YOUR install can launch it and whether a subscription covers it comes from profile.json (model-router-setup.mjs) and your live ~/.codex/models_cache.json"
133
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
156
134
  },
157
135
  {
158
136
  "id": "gpt-5.6-luna",
@@ -162,19 +140,17 @@
162
140
  "tier": "cheap",
163
141
  "costPerMTok": null,
164
142
  "verified": null,
165
- "note": "Codex-harness model; whether YOUR install can launch it and whether a subscription covers it comes from profile.json (model-router-setup.mjs) and your live ~/.codex/models_cache.json"
143
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
166
144
  },
167
145
  {
168
146
  "id": "gpt-5-mini",
169
147
  "provider": "openai",
170
- "harness": [
171
- "codex",
172
- "claude-code"
173
- ],
148
+ "harness": [],
174
149
  "subscription": [],
175
150
  "tier": "mid",
176
151
  "costPerMTok": null,
177
- "verified": null
152
+ "verified": null,
153
+ "note": "Historical model retained for inventory only. No default execution eligibility in this refreshed template."
178
154
  },
179
155
  {
180
156
  "id": "x-ai/grok-4.5",
@@ -230,6 +206,104 @@
230
206
  },
231
207
  "verified": "2026-07-12 OpenRouter /models live (direct API check)",
232
208
  "note": "LANDSCAPE-ONLY: 295B MoE Apache-2.0, strong generalist, second-tier coder (SWE-bench V 78.0 vs GLM-5.2 84.2, vendor numbers). IMPORTANT: this is the PAID slug on purpose \u2014 the tencent/hy3:free promo window closes ~2026-07-20 and would silently die/bill; never wire the :free slug. See goldie/2026-07-12.md Q3."
209
+ },
210
+ {
211
+ "id": "claude-sonnet-5-5",
212
+ "provider": "anthropic",
213
+ "harness": [
214
+ "claude-code"
215
+ ],
216
+ "subscription": [
217
+ "claude-code"
218
+ ],
219
+ "tier": "mid",
220
+ "costPerMTok": {
221
+ "in": 2,
222
+ "out": 10
223
+ },
224
+ "verified": "2026-10-04 actual native subscription launch, exact modelUsage identity and MODEL_OK receipt; official API prices",
225
+ "note": "Verified native Claude subscription launch on the current machine. Per-user coverage still requires the router profile; higher effort settings are recommendations, not benchmark results."
226
+ },
227
+ {
228
+ "id": "claude-opus-5-5",
229
+ "provider": "anthropic",
230
+ "harness": [
231
+ "claude-code"
232
+ ],
233
+ "subscription": [
234
+ "claude-code"
235
+ ],
236
+ "tier": "frontier",
237
+ "costPerMTok": {
238
+ "in": 4,
239
+ "out": 20
240
+ },
241
+ "verified": "2026-10-04 actual native subscription launch, exact modelUsage identity and MODEL_OK receipt; official API prices",
242
+ "note": "Verified native Claude subscription launch on the current machine. Per-user coverage still requires the router profile; higher effort settings are recommendations, not benchmark results."
243
+ },
244
+ {
245
+ "id": "claude-fable-5-1",
246
+ "provider": "anthropic",
247
+ "harness": [],
248
+ "subscription": [],
249
+ "tier": "frontier",
250
+ "costPerMTok": {
251
+ "in": 10,
252
+ "out": 50
253
+ },
254
+ "verified": "2026-10-04 official provider documentation + fresh native inventory",
255
+ "note": "Landscape only until native launch/coverage is verified. Account catalog presence or historical observation is not a fresh execution proof."
256
+ },
257
+ {
258
+ "id": "gpt-6.1-sol",
259
+ "provider": "openai",
260
+ "harness": [
261
+ "codex"
262
+ ],
263
+ "subscription": [
264
+ "codex"
265
+ ],
266
+ "tier": "mid",
267
+ "costPerMTok": {
268
+ "in": 2,
269
+ "out": 10
270
+ },
271
+ "verified": "2026-10-04 actual native Codex subscription launch, requested model, completed turn and MODEL_OK receipt; official API prices",
272
+ "note": "Verified native Codex subscription launch on current machine. Coverage remains profile-scoped; selection does not switch the parent conversation."
273
+ },
274
+ {
275
+ "id": "gpt-6-luna",
276
+ "provider": "openai",
277
+ "harness": [
278
+ "codex"
279
+ ],
280
+ "subscription": [
281
+ "codex"
282
+ ],
283
+ "tier": "cheap",
284
+ "costPerMTok": {
285
+ "in": 0.1,
286
+ "out": 0.5
287
+ },
288
+ "verified": "2026-10-04 actual native Codex subscription launch, requested model, completed turn and MODEL_OK receipt; official API prices",
289
+ "note": "Verified native Codex subscription launch on current machine. Coverage remains profile-scoped; selection does not switch the parent conversation."
290
+ },
291
+ {
292
+ "id": "gpt-6-astra",
293
+ "provider": "openai",
294
+ "harness": [
295
+ "codex"
296
+ ],
297
+ "subscription": [
298
+ "codex"
299
+ ],
300
+ "tier": "frontier",
301
+ "costPerMTok": {
302
+ "in": 10,
303
+ "out": 50
304
+ },
305
+ "verified": "2026-10-04 actual native Codex subscription launch, requested model, completed turn and MODEL_OK receipt; official API prices",
306
+ "note": "Verified native Codex subscription launch on current machine. Coverage remains profile-scoped; selection does not switch the parent conversation."
233
307
  }
234
308
  ]
235
309
  }