ruvnet-brain 4.0.6 โ 4.0.8
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.
- package/README.md +3 -3
- package/bin/install.mjs +118 -9
- package/config/model-router/catalog.template.json +17 -2
- package/console/app.js +32 -14
- package/console/architecture.html +1 -1
- package/data/model-catalog.json +54 -0
- package/package.json +2 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/hooks/codex-hooks.json +17 -17
- package/plugin/hooks/hooks.json +34 -1
- package/plugin/scripts/codex-hook-wrapper.mjs +72 -1
- package/plugin/scripts/ground-ruvnet.sh +2 -1
- package/plugin/scripts/hook-shim.mjs +13 -6
- package/plugin/scripts/lesson-provenance.mjs +21 -0
- package/plugin/scripts/lesson-store.mjs +33 -3
- package/plugin/scripts/route-dispatch.sh +23 -49
- package/plugin/scripts/session-snapshot-contract.mjs +100 -0
- package/plugin/scripts/session-snapshot-hook.mjs +34 -0
- package/plugin/scripts/session-start-core.mjs +25 -7
- package/plugin/scripts/swarm-slot-recycler.mjs +89 -0
- package/plugin/skills/release-proof/SKILL.md +4 -4
- package/plugin/skills/release-proof/scripts/release-proof.mjs +12 -5
- package/plugin/skills/ruvnet-brain/PLAYBOOK.md +19 -4
- package/scripts/candidate-host-evidence.mjs +49 -0
- package/scripts/capability-registry.mjs +3 -3
- package/scripts/ci/learning-replay-codex-adapter.mjs +13 -1
- package/scripts/learning-replay-contract.mjs +14 -4
- package/scripts/learning-replay-execution.mjs +4 -3
- package/scripts/learning-replay-fixture.mjs +23 -2
- package/scripts/lesson-lifecycle.mjs +2 -1
- package/scripts/lesson-ratify.mjs +6 -3
- package/scripts/lesson-seed.mjs +21 -16
- package/scripts/model-router-catalog.mjs +16 -0
- package/scripts/model-router-engine.mjs +4 -1
- package/scripts/onboarding-console.mjs +52 -14
- package/scripts/provider-availability.mjs +12 -0
- package/scripts/publication-receipt.mjs +126 -34
- package/scripts/release-authority.mjs +14 -2
- package/scripts/release-evidence-aggregate.mjs +55 -0
- package/scripts/release-payload.mjs +100 -0
- package/scripts/release-transaction-provider.mjs +413 -0
- package/scripts/release-transaction.mjs +404 -0
- package/scripts/release.mjs +97 -280
- package/scripts/selfcheck.mjs +20 -5
- package/scripts/session-snapshot-contract.mjs +1 -0
- package/scripts/staged-host-verifier.mjs +103 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# ๐ง RuvNet Brain
|
|
6
6
|
|
|
7
|
-
### ๐ง RuvNet Brain โ [](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
|
|
8
8
|
|
|
9
9
|
**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.**
|
|
10
10
|
|
|
@@ -247,11 +247,11 @@ Our code now does **one honest job**: a price transform. A model your subscripti
|
|
|
247
247
|
|
|
248
248
|
Result: **11 jobs supervised, every one producing a fresh successful receipt.** One had been *totally blind* โ writing zero bytes on a healthy day, so "ran fine" and "never ran" were indistinguishable. Cured without changing a line of its logic.
|
|
249
249
|
|
|
250
|
-
### 3.
|
|
250
|
+
### 3. Inherited subagent models are now auditable
|
|
251
251
|
|
|
252
252
|
**A subagent inherits your session's model unless something says otherwise.** Ten agents on an Opus session are ten Opus agents; on a Fable session that's `$10/$50` per Mtok โ up to **10ร what the same mechanical work costs on Haiku**. That single default was the biggest cost leak in the harness, and an advisory rule did not fix it (the router's entire first life saved **$0.018**).
|
|
253
253
|
|
|
254
|
-
|
|
254
|
+
The hook records both explicit-model and inherited-model dispatches so routing is *auditable* rather than merely claimed. It does not claim to block Agent/Task calls: Claude Code 2.1.220 consumes those `PreToolUse` results only after `tool_dispatch_end` ([issue #84](https://github.com/stuinfla/ruvnet-brain/issues/84), [upstream #83195](https://github.com/anthropics/claude-code/issues/83195)). Forks still inherit โ that's what a fork is.
|
|
255
255
|
|
|
256
256
|
> **`npm run falsify`** โ the adversary. Every question that had to be asked of this project ("is the nightly *actually* running?", "is that really rUv's code?", "why is my quota still burning?", "is CI *actually* green?") is now a check that fails on an **unproven claim**, not merely on broken code. Because tests you wrote yourself passing is circular evidence.
|
|
257
257
|
|
package/bin/install.mjs
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
requiredEmbedderModels,
|
|
25
25
|
missingEmbedderModels,
|
|
26
26
|
} from '../kb/model-requirements.mjs';
|
|
27
|
+
import { mergeManagedCatalog } from '../scripts/model-router-catalog.mjs';
|
|
27
28
|
|
|
28
29
|
// SEC-0010 #6 โ the Ed25519 PUBLIC key is EMBEDDED here (not a separate file) so the installer's
|
|
29
30
|
// trust root travels with the installer code itself: an attacker who swaps the downloaded bundle
|
|
@@ -616,6 +617,7 @@ export function beginConsoleRuntimeTransaction(cacheDir, sourceRoot = REPO_ROOT)
|
|
|
616
617
|
['console', 'console'],
|
|
617
618
|
['scripts', 'scripts'],
|
|
618
619
|
['plugin/scripts', 'plugin/scripts'],
|
|
620
|
+
['data/model-catalog.json', 'data/model-catalog.json'],
|
|
619
621
|
['kb/brain-profile.mjs', 'kb/brain-profile.mjs'],
|
|
620
622
|
['bin/install.mjs', 'bin/install.mjs'],
|
|
621
623
|
['package.json', 'package.json'],
|
|
@@ -642,6 +644,15 @@ export function beginConsoleRuntimeTransaction(cacheDir, sourceRoot = REPO_ROOT)
|
|
|
642
644
|
fs.rmSync(staged, { recursive: true, force: true });
|
|
643
645
|
throw new Error(`console runtime version ${packageManifest.version || '(missing)'} does not match candidate ${PACKAGE_VERSION}`);
|
|
644
646
|
}
|
|
647
|
+
try {
|
|
648
|
+
const providerCatalog = JSON.parse(fs.readFileSync(path.join(staged, 'data', 'model-catalog.json'), 'utf8'));
|
|
649
|
+
if (!providerCatalog.providers || typeof providerCatalog.providers !== 'object' || !Object.keys(providerCatalog.providers).length) {
|
|
650
|
+
throw new Error('providers object is missing or empty');
|
|
651
|
+
}
|
|
652
|
+
} catch (error) {
|
|
653
|
+
fs.rmSync(staged, { recursive: true, force: true });
|
|
654
|
+
throw new Error(`console runtime model-catalog is invalid: ${error.message}`);
|
|
655
|
+
}
|
|
645
656
|
const stagedEntry = path.join(staged, 'scripts', 'onboarding-console.mjs');
|
|
646
657
|
for (const syntaxTarget of [stagedEntry, path.join(staged, 'bin', 'install.mjs')]) {
|
|
647
658
|
const checked = spawnSync(process.execPath, ['--check', syntaxTarget], { encoding: 'utf8' });
|
|
@@ -791,7 +802,8 @@ function wirePlugin({ expectedVersion = PACKAGE_VERSION, requireManaged = false
|
|
|
791
802
|
'Wiring the Claude Code plugin',
|
|
792
803
|
'this registers search_ruvnet + the grounding hook so Claude uses the brain automatically',
|
|
793
804
|
);
|
|
794
|
-
const
|
|
805
|
+
const marketplaceSource = process.env.RUVNET_CLAUDE_MARKETPLACE_SOURCE || 'stuinfla/ruvnet-brain';
|
|
806
|
+
const manualMarketplace = `claude plugin marketplace add ${marketplaceSource}`;
|
|
795
807
|
const manualInstall = 'claude plugin install ruvnet-brain@ruvnet-brain --scope user';
|
|
796
808
|
|
|
797
809
|
if (!have('claude')) {
|
|
@@ -809,7 +821,7 @@ function wirePlugin({ expectedVersion = PACKAGE_VERSION, requireManaged = false
|
|
|
809
821
|
if (requireManaged && !before.managed) return { host: false, wired: false, action: 'unmanaged' };
|
|
810
822
|
const addedMarket = before.managed
|
|
811
823
|
? tryRun('claude', ['plugin', 'marketplace', 'update', 'ruvnet-brain'])
|
|
812
|
-
: tryRun('claude', ['plugin', 'marketplace', 'add',
|
|
824
|
+
: tryRun('claude', ['plugin', 'marketplace', 'add', marketplaceSource]);
|
|
813
825
|
// Deliberately NOT reassuring here. This used to say "it may already be added โ that's fine",
|
|
814
826
|
// which is a GUESS about someone else's machine, and when it was wrong the user finished the
|
|
815
827
|
// install with a working search_ruvnet, no slash commands, and a message telling them all was
|
|
@@ -1118,11 +1130,27 @@ const CODEX_PLUGIN_ID = 'ruvnet-brain@ruvnet-brain';
|
|
|
1118
1130
|
const CODEX_MARKETPLACE = 'ruvnet-brain';
|
|
1119
1131
|
const CODEX_MARKETPLACE_SOURCE = 'stuinfla/ruvnet-brain';
|
|
1120
1132
|
|
|
1121
|
-
function
|
|
1122
|
-
|
|
1133
|
+
function codexMarketplaceTarget() {
|
|
1134
|
+
return path.join(
|
|
1123
1135
|
process.env.RUVNET_BRAIN_HOME || path.join(os.homedir(), '.cache', 'ruvnet-brain'),
|
|
1124
1136
|
'codex-marketplace',
|
|
1125
1137
|
);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function codexMarketplaceReady(target = codexMarketplaceTarget()) {
|
|
1141
|
+
try {
|
|
1142
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(target, '.claude-plugin', 'marketplace.json'), 'utf8'));
|
|
1143
|
+
return manifest?.name === CODEX_MARKETPLACE
|
|
1144
|
+
&& Array.isArray(manifest.plugins)
|
|
1145
|
+
&& manifest.plugins.some((plugin) => plugin?.name === 'ruvnet-brain' && plugin?.source === './plugin')
|
|
1146
|
+
&& fs.existsSync(path.join(target, 'plugin', '.codex-plugin', 'plugin.json'));
|
|
1147
|
+
} catch {
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
function prepareCodexMarketplace() {
|
|
1153
|
+
const target = codexMarketplaceTarget();
|
|
1126
1154
|
const staged = `${target}.tmp-${process.pid}`;
|
|
1127
1155
|
fs.rmSync(staged, { recursive: true, force: true });
|
|
1128
1156
|
fs.mkdirSync(path.join(staged, '.claude-plugin'), { recursive: true });
|
|
@@ -1199,6 +1227,18 @@ export function wireCodexPlugin({
|
|
|
1199
1227
|
} = {}) {
|
|
1200
1228
|
if (!fs.existsSync(codexDir)) return { host: false, action: 'no-host' };
|
|
1201
1229
|
const options = { codexBin, codexHome, cwd };
|
|
1230
|
+
// Codex loads every configured marketplace before it can report plugin state. Repair only an
|
|
1231
|
+
// absent/malformed Brain-owned snapshot before that probe, or the probe can fail before its own
|
|
1232
|
+
// repair path is reachable. A healthy current cache stays byte-untouched, and plugin enablement
|
|
1233
|
+
// is still read and preserved below.
|
|
1234
|
+
let localMarketplace = null;
|
|
1235
|
+
if (runJson === runCodexJson && !codexMarketplaceReady()) {
|
|
1236
|
+
try { localMarketplace = prepareCodexMarketplace(); }
|
|
1237
|
+
catch (error) {
|
|
1238
|
+
if (announce) warn(`Codex marketplace preparation failed โ ${error.message}`);
|
|
1239
|
+
return { host: true, action: 'marketplace-prepare-failed', error: error.message };
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1202
1242
|
const before = codexPluginStatus({ ...options, runJson });
|
|
1203
1243
|
if (!before.available) {
|
|
1204
1244
|
if (announce) warn(`Codex plugin lifecycle not installed โ ${before.error}`);
|
|
@@ -1213,7 +1253,13 @@ export function wireCodexPlugin({
|
|
|
1213
1253
|
return { host: true, action: 'unchanged', ...before };
|
|
1214
1254
|
}
|
|
1215
1255
|
|
|
1216
|
-
|
|
1256
|
+
if (runJson === runCodexJson && !localMarketplace) {
|
|
1257
|
+
try { localMarketplace = prepareCodexMarketplace(); }
|
|
1258
|
+
catch (error) {
|
|
1259
|
+
if (announce) warn(`Codex marketplace preparation failed โ ${error.message}`);
|
|
1260
|
+
return { host: true, action: 'marketplace-prepare-failed', error: error.message };
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1217
1263
|
const markets = runJson(['plugin', 'marketplace', 'list', '--json'], options);
|
|
1218
1264
|
if (!markets.ok) {
|
|
1219
1265
|
if (announce) warn(`Codex marketplace check failed โ ${markets.error}`);
|
|
@@ -1728,6 +1774,21 @@ async function doctor() {
|
|
|
1728
1774
|
warn('brain not found here โ run the installer first: npx ruvnet-brain');
|
|
1729
1775
|
return 1; // "not installed" is a FAILING doctor, not a neutral one
|
|
1730
1776
|
}
|
|
1777
|
+
const convergencePath = path.join(process.env.RUVNET_BRAIN_HOME || path.join(os.homedir(), '.cache', 'ruvnet-brain'), 'host-convergence.json');
|
|
1778
|
+
let hostConvergence = { healthy: true, state: 'not-recorded' };
|
|
1779
|
+
if (fs.existsSync(convergencePath)) {
|
|
1780
|
+
try {
|
|
1781
|
+
hostConvergence = classifyHostConvergence(JSON.parse(fs.readFileSync(convergencePath, 'utf8')));
|
|
1782
|
+
if (hostConvergence.healthy) ok(`host convergence receipt: ${hostConvergence.state}`);
|
|
1783
|
+
else {
|
|
1784
|
+
warn(`host convergence incomplete: ${hostConvergence.state}`);
|
|
1785
|
+
info(`Retry the same generation: ${c.bold('npx ruvnet-brain --update')}${hostConvergence.action ? `; ${hostConvergence.action}` : ''}`);
|
|
1786
|
+
}
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
hostConvergence = { healthy: false, state: 'invalid-receipt', error: error.message };
|
|
1789
|
+
warn(`host convergence receipt is invalid: ${error.message}`);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1731
1792
|
have('node') ? ok('node present') : warn('node missing');
|
|
1732
1793
|
have('npm') ? ok('npm present') : warn('npm missing');
|
|
1733
1794
|
have('claude') ? ok('claude CLI present') : warn('claude CLI missing (plugin wiring needs it)');
|
|
@@ -1901,13 +1962,18 @@ async function doctor() {
|
|
|
1901
1962
|
&& !codexLifecycleGuidance(codexLifecycle).healthy
|
|
1902
1963
|
&& !codexLifecycleGuidance(codexLifecycle).intentional,
|
|
1903
1964
|
);
|
|
1965
|
+
// A staged release verifier can execute the packaged hooks under Codex's explicit automation
|
|
1966
|
+
// trust bypass. That proves the real hook commands without pretending a fresh interactive user
|
|
1967
|
+
// has already reviewed them; normal end-user doctor runs remain fail-closed on pending trust.
|
|
1968
|
+
const codexTrustBypassed = process.env.RUVNET_CODEX_HOOK_TRUST_MODE === 'bypass';
|
|
1904
1969
|
const codexWiringFailed = Boolean(cx.host && !cx.wired);
|
|
1905
1970
|
const codexReadinessFailed = Boolean(codexMcp?.blocking);
|
|
1906
1971
|
const failed = (hookResult ? hookResult.exitCode !== 0 : !allGreen)
|
|
1907
1972
|
|| groundingUnprovenPersisted
|
|
1908
|
-
|| codexLifecycleFailed
|
|
1973
|
+
|| (codexLifecycleFailed && !codexTrustBypassed)
|
|
1909
1974
|
|| codexWiringFailed
|
|
1910
1975
|
|| codexReadinessFailed
|
|
1976
|
+
|| !hostConvergence.healthy
|
|
1911
1977
|
|| Boolean(rufloOperational && !rufloOperational.healthy);
|
|
1912
1978
|
if (failed && !hookResult && !groundingUnprovenPersisted) {
|
|
1913
1979
|
console.log(` ${c.red('โ FAILING')} โ the warnings above are real. Re-run ${c.bold('npx ruvnet-brain')} to repair.`);
|
|
@@ -1937,7 +2003,7 @@ async function runSelfCheck({ installState = null, quiet = false } = {}) {
|
|
|
1937
2003
|
mod = await import(new URL('../scripts/selfcheck.mjs', import.meta.url).href);
|
|
1938
2004
|
} catch (e) {
|
|
1939
2005
|
warn(`self-check could not run (${e && e.message}) โ this install has NOT been verified end to end`);
|
|
1940
|
-
return { exitCode: 0, violations: [], lines: [], unavailable: true };
|
|
2006
|
+
return { exitCode: FLAG_DOCTOR_HOOKS ? 1 : 0, violations: [], lines: [], unavailable: true };
|
|
1941
2007
|
}
|
|
1942
2008
|
const result = await mod.selfCheck({ installState, security: true });
|
|
1943
2009
|
if (!quiet || result.violations.length) console.log(mod.formatVerdict(result, { color: c }));
|
|
@@ -2243,7 +2309,29 @@ export function syncHostsAfterUpdate(cacheDir = resolvedKbDir(), {
|
|
|
2243
2309
|
}
|
|
2244
2310
|
}
|
|
2245
2311
|
if (!okApplied) return fail({ applyStatus: applied.status, error: applied.error?.message || 'Stable Spine activation failed' });
|
|
2246
|
-
|
|
2312
|
+
const convergence = classifyHostConvergence({
|
|
2313
|
+
desiredVersion: PACKAGE_VERSION,
|
|
2314
|
+
hosts: {
|
|
2315
|
+
claude: { state: results.claude.host ? 'ready' : 'absent', version: results.claude.version || null },
|
|
2316
|
+
codex: { state: results.codex?.action === 'disabled' ? 'disabled' : (results.codexHost?.host ? 'ready' : 'absent'), version: results.codex?.version || null },
|
|
2317
|
+
},
|
|
2318
|
+
consoleRuntime: results.consoleRuntime,
|
|
2319
|
+
});
|
|
2320
|
+
return { ok: true, convergence, results, applyStatus: applied.status };
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
export function classifyHostConvergence(receipt, expectedVersion = PACKAGE_VERSION) {
|
|
2324
|
+
if (!receipt || receipt.desiredVersion !== expectedVersion) {
|
|
2325
|
+
return { healthy: false, state: 'version-mismatch', action: `required version ${expectedVersion}` };
|
|
2326
|
+
}
|
|
2327
|
+
const hostStates = Object.values(receipt.hosts || {});
|
|
2328
|
+
const badHost = hostStates.find((host) => !['ready', 'disabled', 'absent'].includes(host?.state)
|
|
2329
|
+
|| (host.state === 'ready' && host.version !== expectedVersion));
|
|
2330
|
+
if (badHost) return { healthy: false, state: 'host-pending', action: 're-run host synchronization' };
|
|
2331
|
+
if (receipt.consoleRuntime?.state !== 'ready') {
|
|
2332
|
+
return { healthy: false, state: receipt.consoleRuntime?.state || 'console-unproven', action: 'restart Console, then re-run --doctor' };
|
|
2333
|
+
}
|
|
2334
|
+
return { healthy: true, state: 'channels-converged' };
|
|
2247
2335
|
}
|
|
2248
2336
|
|
|
2249
2337
|
function runUpdate() {
|
|
@@ -2890,7 +2978,28 @@ export async function offerRouterProfile() {
|
|
|
2890
2978
|
for (const [src, dst] of [['catalog.template.json', 'catalog.json'], ['policy.default.mjs', 'policy.default.mjs']]) {
|
|
2891
2979
|
const s = path.join(pkgRoot, 'config', 'model-router', src);
|
|
2892
2980
|
const d = path.join(routerDir, dst);
|
|
2893
|
-
if (fs.existsSync(s)
|
|
2981
|
+
if (!fs.existsSync(s)) continue;
|
|
2982
|
+
if (!fs.existsSync(d)) {
|
|
2983
|
+
fs.copyFileSync(s, d);
|
|
2984
|
+
ok(`installed ${dst} (edit freely โ goldie keeps prices fresh where scheduled)`);
|
|
2985
|
+
continue;
|
|
2986
|
+
}
|
|
2987
|
+
if (src === 'catalog.template.json') {
|
|
2988
|
+
try {
|
|
2989
|
+
const existing = JSON.parse(fs.readFileSync(d, 'utf8'));
|
|
2990
|
+
const managed = JSON.parse(fs.readFileSync(s, 'utf8'));
|
|
2991
|
+
const merged = mergeManagedCatalog(existing, managed);
|
|
2992
|
+
if (merged !== existing) {
|
|
2993
|
+
fs.copyFileSync(d, `${d}.pre-managed-merge`);
|
|
2994
|
+
const tmp = `${d}.tmp-${process.pid}`;
|
|
2995
|
+
fs.writeFileSync(tmp, `${JSON.stringify(merged, null, 2)}\n`, { mode: 0o600 });
|
|
2996
|
+
fs.renameSync(tmp, d);
|
|
2997
|
+
ok(`merged ${merged.candidates.length - existing.candidates.length} managed subscription model(s); your catalog overrides were preserved`);
|
|
2998
|
+
}
|
|
2999
|
+
} catch (error) {
|
|
3000
|
+
warn(`managed model additions were not merged (${error.message}); your existing catalog was left unchanged`);
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
2894
3003
|
}
|
|
2895
3004
|
let copied = 0;
|
|
2896
3005
|
// dispatch-receipt + metaharness-receipts added 2026-07-13: without the LOGGER, subagent routing is
|
|
@@ -1,6 +1,7 @@
|
|
|
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
|
-
"
|
|
3
|
+
"managedVersion": 2,
|
|
4
|
+
"updated": "2026-08-02 (managed template)",
|
|
4
5
|
"candidates": [
|
|
5
6
|
{
|
|
6
7
|
"id": "claude-haiku-4-5-20251001",
|
|
@@ -45,6 +46,20 @@
|
|
|
45
46
|
},
|
|
46
47
|
"verified": "2026-07-07 route-cheap.mjs FRONTIER (API price; $0 under Max)"
|
|
47
48
|
},
|
|
49
|
+
{
|
|
50
|
+
"id": "claude-opus-5",
|
|
51
|
+
"provider": "anthropic",
|
|
52
|
+
"harness": [
|
|
53
|
+
"claude-code"
|
|
54
|
+
],
|
|
55
|
+
"subscription": [
|
|
56
|
+
"claude-code"
|
|
57
|
+
],
|
|
58
|
+
"tier": "frontier",
|
|
59
|
+
"costPerMTok": null,
|
|
60
|
+
"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."
|
|
62
|
+
},
|
|
48
63
|
{
|
|
49
64
|
"id": "claude-fable-5",
|
|
50
65
|
"provider": "anthropic",
|
|
@@ -217,4 +232,4 @@
|
|
|
217
232
|
"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."
|
|
218
233
|
}
|
|
219
234
|
]
|
|
220
|
-
}
|
|
235
|
+
}
|
package/console/app.js
CHANGED
|
@@ -2099,17 +2099,24 @@ function renderRouterEngine(re) {
|
|
|
2099
2099
|
const pool = Array.isArray(re.pool) ? re.pool : [];
|
|
2100
2100
|
const TIER_ORDER = { mechanical: 0, cheap: 1, mid: 2, frontier: 3 };
|
|
2101
2101
|
const byTier = (a, b) => (TIER_ORDER[a.tier] ?? 9) - (TIER_ORDER[b.tier] ?? 9);
|
|
2102
|
-
const
|
|
2102
|
+
const recommended = (Array.isArray(re.decisions) ? re.decisions : []).find((decision) => decision && decision.model);
|
|
2103
|
+
const lensTable = (rows, costOf, costHead, { showRouting = false } = {}) => el('div', { class: 'scroll-x' },
|
|
2103
2104
|
el('table', { class: 'tb rp-tb' },
|
|
2104
2105
|
el('thead', {}, el('tr', {},
|
|
2105
2106
|
el('th', { scope: 'col' }, 'Bucket'), el('th', { scope: 'col' }, 'Model'),
|
|
2106
|
-
el('th', { scope: 'col' }, costHead)
|
|
2107
|
+
el('th', { scope: 'col' }, costHead),
|
|
2108
|
+
showRouting ? el('th', { scope: 'col' }, 'Routing') : null)),
|
|
2107
2109
|
el('tbody', {}, rows.map((p) => el('tr', {},
|
|
2108
2110
|
el('td', { class: 'rp-band' }, p.tier || 'โ'),
|
|
2109
2111
|
el('td', {}, el('div', { class: 'rp-model' }, prettyModel(p.id))),
|
|
2110
2112
|
el('td', { class: 'cell-mono num' }, costOf(p)),
|
|
2113
|
+
showRouting ? el('td', {}, recommended?.model === p.id
|
|
2114
|
+
? chip('last selected', 'green', recommended.reason || 'The latest router receipt selected this model.')
|
|
2115
|
+
: 'available') : null,
|
|
2111
2116
|
)))));
|
|
2112
|
-
// Development
|
|
2117
|
+
// Development is an inventory, not a recommendation filter. The previous one-row-per-tier
|
|
2118
|
+
// projection hid equal-cost Fable behind Opus based solely on array order. Every launchable row
|
|
2119
|
+
// remains visible; the latest actual routing receipt is a separate marker.
|
|
2113
2120
|
const bestPerTier = (rows, price) => {
|
|
2114
2121
|
const seen = {};
|
|
2115
2122
|
for (const p of rows) {
|
|
@@ -2118,9 +2125,9 @@ function renderRouterEngine(re) {
|
|
|
2118
2125
|
}
|
|
2119
2126
|
return Object.values(seen).sort(byTier);
|
|
2120
2127
|
};
|
|
2121
|
-
const devRows =
|
|
2122
|
-
|
|
2123
|
-
(
|
|
2128
|
+
const devRows = pool
|
|
2129
|
+
.filter((p) => (p.harness || []).includes('claude-code'))
|
|
2130
|
+
.sort((a, b) => byTier(a, b) || String(a.id).localeCompare(String(b.id)));
|
|
2124
2131
|
const prodRows = bestPerTier(
|
|
2125
2132
|
pool.filter((p) => p.listPerMTok != null && p.provider !== 'local'),
|
|
2126
2133
|
(p) => p.listPerMTok ?? Infinity);
|
|
@@ -2130,7 +2137,7 @@ function renderRouterEngine(re) {
|
|
|
2130
2137
|
el('span', { class: 'rp-obj' }, 'you, in Claude Code โ models your plan covers win at $0 marginal')),
|
|
2131
2138
|
lensTable(devRows,
|
|
2132
2139
|
(p) => (p.subscriptionCovered ? el('b', { title: 'covered by your subscription โ zero marginal cost' }, '$0 ยท yours') : money(p.marginalPerMTok)),
|
|
2133
|
-
'Your cost'));
|
|
2140
|
+
'Your cost', { showRouting: true }));
|
|
2134
2141
|
const prodBlock = el('div', { class: 'rp-profile' },
|
|
2135
2142
|
el('div', { class: 'rp-head' },
|
|
2136
2143
|
el('span', { class: 'rp-name' }, 'Production'),
|
|
@@ -2140,7 +2147,7 @@ function renderRouterEngine(re) {
|
|
|
2140
2147
|
const poolFoot = el('p', { class: 'fineprint' },
|
|
2141
2148
|
re.catalogSource === 'built-in-fallback'
|
|
2142
2149
|
? `No personal catalog found โ showing a minimal built-in set of ${pool.length}. Run \`node scripts/model-router-setup.mjs\` to build your real catalog, then the engine weighs yours on every call.`
|
|
2143
|
-
: `
|
|
2150
|
+
: `All ${devRows.length} launchable Claude Code models are shown. The engine weighs all ${pool.length} candidates; the routing marker comes from its latest receipt, never catalog order.`);
|
|
2144
2151
|
|
|
2145
2152
|
// Decisions: dedupe consecutive identical picks, keep 3, humanize the reason head. The full
|
|
2146
2153
|
// append-only log stays on disk โ this is a pulse, not a table of record.
|
|
@@ -4011,6 +4018,7 @@ function renderLessons(data) {
|
|
|
4011
4018
|
if (c.awaitingYou) chips.push(chip(`${c.awaitingYou} awaiting you`, 'amber', 'Recorded, but not yet agreed to by you. Until you decide, it does not enforce at full strength.'));
|
|
4012
4019
|
if (c.active) chips.push(chip(`${c.active} on`, 'green'));
|
|
4013
4020
|
if (c.off) chips.push(chip(`${c.off} off`, 'grey', 'Switched off by you. The record of where you taught it is kept.'));
|
|
4021
|
+
if (c.quarantined) chips.push(chip(`${c.quarantined} imported`, 'nt', 'Maintainer or demonstration history. It is visible for audit but cannot become your personal policy.'));
|
|
4014
4022
|
if (c.blocking) chips.push(chip(`${c.blocking} can stop me`, 'cyan', 'These interrupt me at their moment and I cannot continue until the check passes.'));
|
|
4015
4023
|
setChips('chips-lessons', chips);
|
|
4016
4024
|
|
|
@@ -4022,6 +4030,7 @@ function renderLessons(data) {
|
|
|
4022
4030
|
const box = el('input', {
|
|
4023
4031
|
type: 'checkbox', class: 'lesson-switch', id: `lsw-${r.id}`,
|
|
4024
4032
|
'aria-label': `${isOn ? 'Turn off' : 'Turn on'}: ${r.statement.slice(0, 60)}`,
|
|
4033
|
+
disabled: r.quarantined || null,
|
|
4025
4034
|
});
|
|
4026
4035
|
box.checked = isOn;
|
|
4027
4036
|
|
|
@@ -4073,7 +4082,9 @@ function renderLessons(data) {
|
|
|
4073
4082
|
chip(r.origin, r.userStated ? 'green' : 'nt',
|
|
4074
4083
|
r.userStated
|
|
4075
4084
|
? 'You said this. Only lessons you stated yourself are allowed to reach the strongest level.'
|
|
4076
|
-
:
|
|
4085
|
+
: r.quarantined
|
|
4086
|
+
? 'This came from bundled maintainer or demonstration history. It is not your statement and cannot be turned into your personal policy.'
|
|
4087
|
+
: 'I inferred this from what happened. A lesson I inferred can never be raised to "Stops me", however often it fires โ the model does not get to ratify its own rules.'),
|
|
4077
4088
|
r.taughtCount ? chip(`taught ${r.taughtCount}ร`, 'grey') : null,
|
|
4078
4089
|
r.awaitingYou ? chip('awaiting your decision', 'amber', 'Recorded, but you have not agreed to it yet.') : null,
|
|
4079
4090
|
].filter(Boolean);
|
|
@@ -4097,9 +4108,9 @@ function renderLessons(data) {
|
|
|
4097
4108
|
: null,
|
|
4098
4109
|
r.projects && r.projects.length
|
|
4099
4110
|
? el('p', null, el('strong', null, 'Learned in: '), r.projects.join(', ')) : null,
|
|
4100
|
-
el('p', { class: 'muted' },
|
|
4101
|
-
'
|
|
4102
|
-
'you can switch it back on here at any time.')));
|
|
4111
|
+
el('p', { class: 'muted' }, r.quarantined
|
|
4112
|
+
? 'This imported record is quarantined for audit. It cannot be switched on or ratified as your policy.'
|
|
4113
|
+
: 'Turning this off hides the rule without deleting the record of where you taught it โ you can switch it back on here at any time.')));
|
|
4103
4114
|
|
|
4104
4115
|
list.append(el('div', { class: `cap-row lesson-row${r.demoted ? ' is-off' : ''}` },
|
|
4105
4116
|
// NO `for=` here. The label WRAPS its checkbox, which is already an implicit association; a
|
|
@@ -4142,9 +4153,10 @@ function partitionLessons(list, rows) {
|
|
|
4142
4153
|
// the card whose job is to be the truth about it, and printed BECAUSE they used the control we
|
|
4143
4154
|
// gave them. Found by GPT-5.6-Sol, 2026-07-24. A group's heading must be derivable from the state
|
|
4144
4155
|
// of the rows inside it, which is why the counts below are computed from the split, never passed in.
|
|
4145
|
-
const asks = [], inForce = [], off = [];
|
|
4156
|
+
const asks = [], inForce = [], off = [], quarantined = [];
|
|
4146
4157
|
rows.forEach((r, i) => {
|
|
4147
|
-
if (r.
|
|
4158
|
+
if (r.quarantined) quarantined.push(kids[i]);
|
|
4159
|
+
else if (r.demoted) off.push(kids[i]);
|
|
4148
4160
|
else if (r.awaitingYou) asks.push(kids[i]);
|
|
4149
4161
|
else inForce.push(kids[i]);
|
|
4150
4162
|
});
|
|
@@ -4171,6 +4183,12 @@ function partitionLessons(list, rows) {
|
|
|
4171
4183
|
`${off.length} ${off.length === 1 ? 'rule you switched off' : 'rules you switched off'} โ not in force; switch back on any time`),
|
|
4172
4184
|
el('div', { class: 'cap-list' }, ...off)));
|
|
4173
4185
|
}
|
|
4186
|
+
if (quarantined.length) {
|
|
4187
|
+
out.push(el('details', { class: 'lessons-more' },
|
|
4188
|
+
el('summary', null,
|
|
4189
|
+
`${quarantined.length} imported maintainer or demonstration ${quarantined.length === 1 ? 'record' : 'records'} โ quarantined, not your policy`),
|
|
4190
|
+
el('div', { class: 'cap-list' }, ...quarantined)));
|
|
4191
|
+
}
|
|
4174
4192
|
return out;
|
|
4175
4193
|
}
|
|
4176
4194
|
|
|
@@ -749,7 +749,7 @@ table.tb { width: 100%; border-collapse: collapse; font-size: var(--fs--1); min-
|
|
|
749
749
|
<tr><td class="cell-mono">SessionStart</td><td class="cell-name">session-start.sh</td><td><span class="part">nudge</span></td><td>Tells you the brain is on. Runs 4 pure-filesystem health checks in under 5 ms โ no cache dir? no <code>.rvf</code> at all? reader deps gone? last real search failed? Injects the capability playbook once per session.</td></tr>
|
|
750
750
|
<tr><td class="cell-mono">UserPromptSubmit</td><td class="cell-name">ground-ruvnet.sh</td><td><span class="part">nudge</span></td><td>Three gates on your prompt: <b>RuvNet</b> (you named the stack โ ground before asserting), <b>drift</b> (you reached for a classical default โ name the rUv replacement), <b>build</b> (a build request โ apply the playbook).</td></tr>
|
|
751
751
|
<tr><td class="cell-mono">PreToolUse<small>Write ยท Edit ยท Bash</small></td><td class="cell-name">hijack-ruvnet.sh</td><td><span class="part">nudge</span></td><td>Scans the payload for four categories of classical default โ generic vector stores, paid embedding APIs, generic RAG frameworks, hand-rolled memory glue โ and names the rUv replacement. <b>Never blocks.</b> One word on line 12 would make it block; it ships as advisory because a false-positive refusal bricks real work.</td></tr>
|
|
752
|
-
<tr><td class="cell-mono">PreToolUse<small>Task</small></td><td class="cell-name">route-dispatch.sh</td><td><span class="
|
|
752
|
+
<tr><td class="cell-mono">PreToolUse<small>Task ยท Agent</small></td><td class="cell-name">route-dispatch.sh</td><td><span class="part">audit</span></td><td>Records declared versus inherited model use. Claude Code 2.1.220 consumes this result after dispatch, so it never claims a late refusal. <b>Opt-in only</b> โ with no router profile on disk it does nothing.</td></tr>
|
|
753
753
|
<tr><td class="cell-mono">PreToolUse<small>Bash</small></td><td class="cell-name">verify-interface.sh</td><td><span class="no">blocks</span></td><td>Refuses an ecosystem CLI subcommand whose <code>--help</code> you have not read in 24 h. Born from reporting AgentDB broken three times when the real defect was passing a query positionally instead of with <code>-q</code>.</td></tr>
|
|
754
754
|
<tr><td class="cell-mono">PreToolUse<small>Bash</small></td><td class="cell-name">design-wall.sh</td><td><span class="no">blocks</span></td><td>Refuses shipping or opening a visual surface with no fresh design grade. <b>Scoped to this repo</b> since issue #17, after a plain <code>git commit</code> in an unrelated project got blocked demanding someone else's ritual.</td></tr>
|
|
755
755
|
<tr><td class="cell-mono">PreToolUse ยท Stop</td><td class="cell-name">lesson-hooks.sh</td><td><span class="part">nudge</span></td><td>Surfaces your own recorded corrections at the moment they apply โ writing code, changing the machine, asserting a fact, claiming done.</td></tr>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"purpose": "Per-provider (house) tier ladders. The FRONTIER โ the escalation target AND the savings baseline โ is personalized to the user's own house: a Claude shop's frontier is Fable 5, a ChatGPT shop's is GPT-5.6 Sol, a Codex shop's is Sol, a Gemini shop's is Gemini 3.1 Pro, a Grok shop's is Grok 4.5. Modeled on ruflo ADR-148 (assets/model-router/openrouter-alts.json), extended with a provider-house axis rUv's registry does not carry.",
|
|
4
|
+
"generated": "2026-07-15",
|
|
5
|
+
"schema_version": 1,
|
|
6
|
+
"sources": {
|
|
7
|
+
"prices": "OpenRouter /api/v1/models live catalog, pulled 2026-07-15 (in/out USD per Mtok).",
|
|
8
|
+
"rankings": "Artificial Analysis Intelligence Index (artificialanalysis.ai) + Arena/LMArena (arena.ai) โ the ONLY independent evaluators carrying current-generation models as of 2026-07-15; each figure cross-verified twice.",
|
|
9
|
+
"provenance_rule": "rUv ADR-206: vendor-reported scores are optimistic and harness-confounded โ trust independent (AA/Arena) numbers, treat vendor self-scaffold SWE-bench/LiveCodeBench figures as noisy features, never as truth.",
|
|
10
|
+
"benchmark_lag": "The canonical hard coding benchmarks (SWE-bench Verified standardized harness, LiveCodeBench, Aider polyglot) were ALL months stale on 2026-07-15 and carry NONE of these models. The '88.6% / 95% SWE-bench' figures in the press are vendor self-scaffold scores, not the standardized harness โ excluded here.",
|
|
11
|
+
"release_refs": "OpenAI GPT-5.6 GA 2026-07-09 (openai.com/index/gpt-5-6); Anthropic Fable 5 2026-06-09 (anthropic.com/news/claude-fable-5-mythos-5); Google Gemini 3.1 Pro 2026-02-19; xAI/SpaceXAI Grok 4.5 2026-07-08."
|
|
12
|
+
},
|
|
13
|
+
"caveat": "Prices are live-verified; tier placements are sensible, independent-benchmark-grounded starters, NOT this user's measured results. Override per-installation via $RUVNET_MODEL_CATALOG or the console. The automated always-current path is rUv's ADR-206 (BenchPress predictor + hourly OpenRouter watcher) โ see scripts/refresh-model-catalog.mjs for the live re-pull.",
|
|
14
|
+
"effort_note": "Frontier effort defaults to 'xhigh' (the max reasoning a shop reaches for on the hardest tasks); this is a principled default, not a per-effort measurement."
|
|
15
|
+
},
|
|
16
|
+
"default_provider": "anthropic",
|
|
17
|
+
"default_provider_reason": "This is a Claude Code plugin, so DEVELOPMENT genuinely runs on Anthropic โ that is a detected fact, not an arbitrary house preference. Set your PRODUCTION house in the console (or $RUVNET_PROVIDER) if your app runs on a different provider; it is never assumed silently.",
|
|
18
|
+
"providers": {
|
|
19
|
+
"anthropic": {
|
|
20
|
+
"label": "Claude (Anthropic)",
|
|
21
|
+
"detect_env": ["ANTHROPIC_API_KEY", "CLAUDE_CODE_ENTRYPOINT", "CLAUDECODE"],
|
|
22
|
+
"frontier": { "model": "claude-fable-5", "in": 10, "out": 50, "released": "2026-06-09", "rank": "AA Intelligence #1 (60) ยท Arena #1 (1508 Elo)", "source": "independent (AA + Arena)" },
|
|
23
|
+
"mid": { "model": "claude-sonnet-5", "in": 2, "out": 10, "released": "2026-06-30", "rank": "AA 53; intro price $2/$10 โ $3/$15 after 2026-08-31", "source": "independent (AA)" },
|
|
24
|
+
"cheap": { "model": "claude-haiku-4.5", "in": 1, "out": 5, "released": "2025-10-15", "rank": "cheap tier (AA index low; cost-cascade prefers a cross-provider value pick when an OpenRouter key is present)", "source": "catalog" }
|
|
25
|
+
},
|
|
26
|
+
"openai": {
|
|
27
|
+
"label": "ChatGPT (OpenAI)",
|
|
28
|
+
"detect_env": ["OPENAI_API_KEY"],
|
|
29
|
+
"frontier": { "model": "openai/gpt-5.6-sol", "in": 5, "out": 30, "released": "2026-07-09", "rank": "AA Intelligence #2 (59) ยท AA Coding Index leader (80) ยท Terminal-Bench 2.1 SOTA ยท ~1/3 Fable 5's cost/task", "source": "independent (AA)" },
|
|
30
|
+
"mid": { "model": "openai/gpt-5.6-terra", "in": 1, "out": 6, "released": "2026-07-09", "rank": "AA Coding 77.4 โ beats prev-gen flagship GPT-5.5 (76.4) at lower live catalog pricing", "source": "independent (AA)" },
|
|
31
|
+
"cheap": { "model": "openai/gpt-5.6-luna", "in": 0.1, "out": 0.6, "released": "2026-07-09", "rank": "AA 51 ยท Coding 74.6 โ beats most last-gen mid-tier", "source": "independent (AA)" }
|
|
32
|
+
},
|
|
33
|
+
"codex": {
|
|
34
|
+
"label": "Codex (OpenAI)",
|
|
35
|
+
"detect_env": [],
|
|
36
|
+
"aliasOf": "openai",
|
|
37
|
+
"note": "Codex folded into the GPT-5.6 Sol/Terra/Luna tiers on 2026-07-09 โ there is NO separate '-codex' SKU this generation; gpt-5.3-codex (2026-02-05) was the last dedicated one and is superseded. A Codex shop's frontier IS Sol."
|
|
38
|
+
},
|
|
39
|
+
"google": {
|
|
40
|
+
"label": "Gemini (Google)",
|
|
41
|
+
"detect_env": ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
42
|
+
"frontier": { "model": "google/gemini-3.1-pro-preview", "in": 2, "out": 12, "released": "2026-02-19", "rank": "AA Intelligence 46 โ BELOW the Anthropic/OpenAI/xAI frontier cluster (54โ60); still preview-named. Honest: Gemini is not frontier-competitive on independent indices right now.", "source": "independent (AA) โ vendor's GPQA-D 94.3% / ARC-AGI-2 77.1% are Google's own, uncorroborated" },
|
|
43
|
+
"mid": { "model": "google/gemini-3.5-flash", "in": 1.5, "out": 9, "released": "2026", "rank": "AA 50 โ out-scores Google's own 3.1 Pro on this index (real anomaly, not a typo)", "source": "independent (AA)" },
|
|
44
|
+
"cheap": { "model": "google/gemini-3.1-flash-lite", "in": 0.25, "out": 1.5, "released": "2026", "rank": "budget", "source": "catalog" }
|
|
45
|
+
},
|
|
46
|
+
"xai": {
|
|
47
|
+
"label": "Grok (SpaceXAI)",
|
|
48
|
+
"detect_env": ["XAI_API_KEY", "GROK_API_KEY"],
|
|
49
|
+
"note": "xAI's live lineup has no distinct budget SKU below Grok 4.3 as of 2026-07-15 (the 'grok-4.1-fast' I first wrote does not exist in the live catalog โ the verify gate caught it). A Grok shop's cheap tasks route to Grok 4.3 or a cross-provider value pick.",
|
|
50
|
+
"frontier": { "model": "x-ai/grok-4.5", "in": 2, "out": 6, "released": "2026-07-08", "rank": "AA Intelligence 54 (#8) at ~1/3 Opus 4.7's blended price โ the frontier VALUE standout; 'Opus-class, faster, cheaper' (Musk)", "source": "independent (AA)" },
|
|
51
|
+
"mid": { "model": "x-ai/grok-4.3", "in": 1.25, "out": 2.5, "released": "2026-04-30", "rank": "AA 38 โ also xAI's cheapest verified stable tier", "source": "independent (AA)" }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruvnet-brain",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.8",
|
|
4
4
|
"description": "One-command installer for RuvNet Brain โ a portable, source-grounded brain over rUv's RuvNet building blocks, delivered as a Claude Code plugin so Claude uses the stack instead of fighting it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"LICENSE",
|
|
69
69
|
"console/",
|
|
70
70
|
"config/model-router/",
|
|
71
|
+
"data/model-catalog.json",
|
|
71
72
|
"scripts/",
|
|
72
73
|
"kb/verify-citation.mjs",
|
|
73
74
|
"kb/zip-extract.mjs",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruvnet-brain",
|
|
3
3
|
"description": "RuvNet brain transplant for Claude Code โ grounds every RuvNet decision in real source across 69 rUv repositories, prefers Ruflo / RuVector-RVF / AgentDB over training-prior defaults (pgvector, Pinecone, hand-rolled cosine), and can pull in any RuvNet repo on demand. Ships an enforced UserPromptSubmit retrieve-and-inject grounding hook that sharply reduces drift.",
|
|
4
|
-
"version": "4.0.
|
|
4
|
+
"version": "4.0.8",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Stuart Kerr"
|
|
7
7
|
},
|