total-recall-brain 3.41.4 → 3.41.6
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/bin/total-recall.mjs +10 -6
- package/docs/reference/cli-reference.md +18 -0
- package/frontend/dist/assets/index-4tGQfTa2.js +5605 -0
- package/frontend/dist/assets/index-DtwsDyFM.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/package.json +11 -0
- package/frontend/src/App.tsx +47 -122
- package/frontend/src/components/plugins/plugin-frame.ts +1 -0
- package/frontend/src/index.css +152 -86
- package/frontend/src/pages/HelpPage.tsx +13 -10
- package/frontend/src/pages/MemoryPage.tsx +28 -30
- package/frontend/src/pages/NetworkPage.tsx +3 -3
- package/frontend/src/pages/PluginsPage.tsx +6 -5
- package/frontend/src/pages/RulesPage.tsx +16 -12
- package/package.json +4 -3
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +3114 -36
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +4 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +3114 -36
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +4 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +3121 -33
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +4 -1
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +3122 -34
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +4 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +3127 -49
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +136 -7
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +135 -26
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +3121 -33
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +228 -9
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +24 -30
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +29 -16
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +73 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +182 -365
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +40 -14
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/agy.mjs +187 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/claude.mjs +275 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cline.mjs +132 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/codex.mjs +187 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cursor.mjs +158 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/devin.mjs +157 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/dsh.mjs +183 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/grok.mjs +132 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +173 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +87 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/openclaw.mjs +173 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/pi.mjs +129 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate.mjs +12 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/json-reply.mjs +22 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +72 -70
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/model-catalog.mjs +195 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/paths.mjs +11 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/pool.mjs +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +169 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +136 -346
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/ssss-store.mjs +20 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/telemetry.mjs +43 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +156 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +117 -35
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +51 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +398 -57
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +304 -46
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +367 -41
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +33 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +159 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +197 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/lint.mjs +153 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +261 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +151 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +114 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +96 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +899 -662
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/openwiki.mjs +8987 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/mermaid-diagrams/SKILL.md +44 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/write-connector/SKILL.md +56 -0
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3097 -81
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +4 -1
- package/src/cli/agent.mjs +12 -1
- package/src/cli/compact.mjs +108 -0
- package/src/cli/connect.mjs +56 -1
- package/src/cli/context.mjs +55 -23
- package/src/cli/doctor.mjs +5 -2
- package/src/cli/forget.mjs +2 -0
- package/src/cli/harness.mjs +62 -50
- package/src/cli/memory-entry.mjs +2 -2
- package/src/cli/preflight.mjs +152 -16
- package/src/cli/release.mjs +386 -0
- package/src/cli/remember.mjs +19 -1
- package/src/cli/rules.mjs +54 -12
- package/src/cli/startup.mjs +1 -1
- package/src/cli/status.mjs +12 -2
- package/src/core/claude-hooks.mjs +41 -3
- package/src/core/cli-commands.mjs +2 -0
- package/src/core/cli-semantic-index.mjs +3 -2
- package/src/core/conflict-detector.mjs +23 -12
- package/src/core/context-compactor.mjs +145 -0
- package/src/core/context-policy.mjs +20 -0
- package/src/core/duplicate-guard.mjs +350 -0
- package/src/core/meta-harness.mjs +31 -11
- package/src/core/preflight-gate.mjs +2 -2
- package/src/core/preflight-receipt.mjs +29 -1
- package/src/core/scaffold-plugins.mjs +7 -0
- package/src/core/stdio-flush.mjs +23 -0
- package/src/core/surface.mjs +33 -10
- package/src/server/invoke-durability.mjs +39 -0
- package/src/server/invoke-write-child.mjs +25 -0
- package/src/server/routes/invoke.mjs +7 -0
- package/src/server/routes/plugins.mjs +2 -2
- package/frontend/dist/assets/index-BIQmqDKP.css +0 -1
- package/frontend/dist/assets/index-CRzjQLu7.js +0 -5631
- package/frontend/src/components/DaemonLogsTab.tsx +0 -54
- package/frontend/src/components/ResearchAgendaTab.tsx +0 -712
- package/frontend/src/components/TaskQueueTab.tsx +0 -193
- package/frontend/src/components/VoiceInput.tsx +0 -112
- package/frontend/src/pages/ChatPage.tsx +0 -1059
- package/frontend/src/pages/CollabPage.tsx +0 -470
- package/frontend/src/pages/DesignDocsPage.tsx +0 -621
- package/frontend/src/pages/FilesPage.tsx +0 -393
- package/frontend/src/pages/HealthPage.tsx +0 -359
- package/frontend/src/pages/InboxPage.tsx +0 -83
- package/frontend/src/pages/InstructionsPage.tsx +0 -583
- package/frontend/src/pages/IntegrationsPage.tsx +0 -761
- package/frontend/src/pages/MeshPage.css +0 -58
- package/frontend/src/pages/MeshPage.tsx +0 -1219
- package/frontend/src/pages/OkfPage.tsx +0 -535
- package/frontend/src/pages/SandboxPage.tsx +0 -113
- package/frontend/src/pages/SkillsPage.tsx +0 -1994
- package/frontend/src/pages/TasksPage.tsx +0 -427
- package/frontend/src/pages/UsagePage.tsx +0 -702
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +0 -65
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +0 -76
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +0 -19
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +0 -266
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +0 -446
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +0 -152
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +0 -71
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +0 -36
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +0 -35
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +0 -175
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +0 -32
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +0 -575
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model and benchmark catalog: a projection of real sources, refreshed out of band.
|
|
3
|
+
* Reads never touch the network and never invent a figure. An unavailable source keeps its
|
|
4
|
+
* last good data and reports its error; a source never fetched is simply absent.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { execFileSync } from 'node:child_process';
|
|
9
|
+
import { derivedDir } from './paths.mjs';
|
|
10
|
+
import { findBinary } from './worker.mjs';
|
|
11
|
+
|
|
12
|
+
const OPENROUTER_URL = 'https://openrouter.ai/api/v1/models';
|
|
13
|
+
const AA_URL = 'https://artificialanalysis.ai/api/v2/language/models/free';
|
|
14
|
+
const SWE_PRO_URL = 'https://labs.scale.com/leaderboard/swe_bench_pro_public';
|
|
15
|
+
const TBENCH_URL = 'https://www.tbench.ai/';
|
|
16
|
+
const EMPTY = () => ({ updatedAt: null, source: 'not-synced', harnessModels: {}, sources: {}, pricing: {}, codingIndex: {}, leaderboards: {} });
|
|
17
|
+
|
|
18
|
+
const catalogFile = () => path.join(derivedDir(), 'model-catalog.json');
|
|
19
|
+
|
|
20
|
+
export function getModelCatalog() {
|
|
21
|
+
try {
|
|
22
|
+
const data = JSON.parse(fs.readFileSync(catalogFile(), 'utf8'));
|
|
23
|
+
if (data?.sources) return { ...EMPTY(), ...data };
|
|
24
|
+
} catch { /* not synced yet */ }
|
|
25
|
+
return EMPTY();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Compare names across catalogs: no provider prefix, no effort suffix, no punctuation. */
|
|
29
|
+
export function normalizeModelName(name) {
|
|
30
|
+
return String(name).toLowerCase().split('/').pop().replace(/\([^)]*\)/g, ' ').replace(/[-_ ](low|medium|high|xhigh|max)\s*$/, '').replace(/[^a-z0-9]/g, '');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function parseOpenRouter(json) {
|
|
34
|
+
const pricing = {};
|
|
35
|
+
for (const model of json?.data || []) {
|
|
36
|
+
const prompt = Number(model.pricing?.prompt), completion = Number(model.pricing?.completion);
|
|
37
|
+
if (!Number.isFinite(prompt) || !Number.isFinite(completion)) continue;
|
|
38
|
+
pricing[normalizeModelName(model.id)] = { id: model.id, promptPer1M: +(prompt * 1e6).toFixed(4), completionPer1M: +(completion * 1e6).toFixed(4), contextWindow: model.context_length ?? null };
|
|
39
|
+
}
|
|
40
|
+
return pricing;
|
|
41
|
+
}
|
|
42
|
+
/** The free tier fills only some indices, so each stays null unless the provider returned it. */
|
|
43
|
+
export function parseArtificialAnalysis(json) {
|
|
44
|
+
const index = {};
|
|
45
|
+
for (const model of json?.data || []) {
|
|
46
|
+
const e = model.evaluations || {};
|
|
47
|
+
const row = { name: model.name, intelligenceIndex: e.artificial_analysis_intelligence_index ?? null, engineeringIndex: e.artificial_analysis_engineering_index ?? null, codingIndex: e.artificial_analysis_coding_index ?? null, agenticIndex: e.artificial_analysis_agentic_index ?? null };
|
|
48
|
+
if ([row.intelligenceIndex, row.engineeringIndex, row.codingIndex, row.agenticIndex].some(value => typeof value === 'number')) index[normalizeModelName(model.slug || model.name)] = row;
|
|
49
|
+
}
|
|
50
|
+
return index;
|
|
51
|
+
}
|
|
52
|
+
/** Server-rendered rows read: rank, model, score, ±, confidence interval. */
|
|
53
|
+
export function parseSweBenchPro(html) {
|
|
54
|
+
const text = String(html).replace(/<(script|style)[^>]*>[\s\S]*?<\/\1>/g, ' ').replace(/<[^>]+>/g, '|').replace(/\s*\|[\s|]*/g, '|');
|
|
55
|
+
return [...text.matchAll(/\|(\d{1,3})\|([^|]{2,80})(?:\|[A-Z]{2,8})?\|(\d{1,3}\.\d+)\|±\|(\d{1,3}\.\d+)(?=\|)/g)].map(m => ({ rank: +m[1], model: m[2].replace(/\*$/, '').trim(), score: +m[3], ci95: +m[4] }));
|
|
56
|
+
}
|
|
57
|
+
/** The page embeds its leaderboard as escaped JSON: a `leaderboard` with a version name, then a `rows` array. */
|
|
58
|
+
export function parseTerminalBench(html) {
|
|
59
|
+
const text = String(html).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
60
|
+
const start = text.indexOf('"rows":[');
|
|
61
|
+
if (start < 0) return { benchmark: null, rows: [] };
|
|
62
|
+
const open = start + '"rows":'.length;
|
|
63
|
+
let depth = 0, inString = false, end = -1;
|
|
64
|
+
for (let i = open; i < text.length; i += 1) {
|
|
65
|
+
const c = text[i];
|
|
66
|
+
if (inString) { if (c === '\\') i += 1; else if (c === '"') inString = false; continue; }
|
|
67
|
+
if (c === '"') inString = true;
|
|
68
|
+
else if (c === '[' || c === '{') depth += 1;
|
|
69
|
+
else if (c === ']' || c === '}') { depth -= 1; if (depth === 0) { end = i; break; } }
|
|
70
|
+
}
|
|
71
|
+
if (end < 0) return { benchmark: null, rows: [] };
|
|
72
|
+
const plain = value => (typeof value === 'string' ? value : value?.label ?? null);
|
|
73
|
+
const rows = JSON.parse(text.slice(open, end + 1)).map(row => ({
|
|
74
|
+
rank: row.rank ?? null, model: plain(row.metadata?.model_display), agent: plain(row.metadata?.agent_display), effort: row.metadata?.reasoning_effort ?? null,
|
|
75
|
+
score: row.metrics?.accuracy ?? null, ci95: row.metrics?.accuracy_ci95_half_width ?? null, costUSD: row.metrics?.total_cost_usd ?? null,
|
|
76
|
+
})).filter(row => row.model && typeof row.score === 'number');
|
|
77
|
+
const version = [...text.slice(0, start).matchAll(/"name":"(\d+)-(\d+)-(\d+)"/g)].at(-1);
|
|
78
|
+
return { benchmark: version ? `${version[1]}.${version[2]}` : null, rows };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function fromSource(previous, name, url, load, count) {
|
|
82
|
+
try {
|
|
83
|
+
const data = await load();
|
|
84
|
+
const size = count(data);
|
|
85
|
+
if (!size) throw new Error('Source returned no parseable rows');
|
|
86
|
+
return { data, status: { ok: true, url, fetchedAt: new Date().toISOString(), count: size } };
|
|
87
|
+
} catch (error) {
|
|
88
|
+
return { data: null, status: { ...(previous.sources?.[name] || {}), ok: false, url, attemptedAt: new Date().toISOString(), error: String(error.message).slice(0, 300) } };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Ask each installed harness for its own model list. One failing does not hide the others. */
|
|
93
|
+
function harnessModelList(run) {
|
|
94
|
+
const readers = {
|
|
95
|
+
agy: ['agy', ['models'], text => text.split('\n').map(line => line.split('\t')).filter(parts => parts.length === 2).map(([id, name]) => ({ id: id.trim(), name: name.trim() }))],
|
|
96
|
+
ollama: ['ollama', ['list'], text => text.split('\n').slice(1).map(line => line.trim().split(/\s+/)[0]).filter(id => id && !/embed|bge-|minilm|rerank/i.test(id)).map(id => ({ id, name: id }))],
|
|
97
|
+
codex: ['codex', ['debug', 'models'], text => (JSON.parse(text).models || []).map(model => ({ id: model.slug, name: model.display_name || model.slug }))],
|
|
98
|
+
};
|
|
99
|
+
const models = {}, errors = {};
|
|
100
|
+
for (const [harness, [bin, args, parse]] of Object.entries(readers)) {
|
|
101
|
+
const binary = findBinary(bin);
|
|
102
|
+
if (!binary) continue;
|
|
103
|
+
try { models[harness] = parse(run(binary, args)); } catch (error) { errors[harness] = String(error.message).slice(0, 200); }
|
|
104
|
+
}
|
|
105
|
+
return { models, errors };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function secretFrom(names) {
|
|
109
|
+
for (const name of names) {
|
|
110
|
+
try {
|
|
111
|
+
const value = execFileSync(process.env.TOTAL_RECALL_BIN || 'total-recall', ['auth', 'secret', 'get', name], { encoding: 'utf8', timeout: 15000, stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
112
|
+
if (value) return value;
|
|
113
|
+
} catch { /* try the next name */ }
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function syncModelCatalog({ fetchImpl = fetch, secret = secretFrom, run = (bin, args) => execFileSync(bin, args, { encoding: 'utf8', timeout: 15000, stdio: ['ignore', 'pipe', 'ignore'] }) } = {}) {
|
|
119
|
+
const previous = getModelCatalog();
|
|
120
|
+
const next = { ...previous, sources: { ...previous.sources } };
|
|
121
|
+
const getJson = async (url, headers = {}) => {
|
|
122
|
+
const response = await fetchImpl(url, { headers, signal: AbortSignal.timeout(30000) });
|
|
123
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
124
|
+
return response.json();
|
|
125
|
+
};
|
|
126
|
+
const getText = async url => {
|
|
127
|
+
const response = await fetchImpl(url, { signal: AbortSignal.timeout(30000) });
|
|
128
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
129
|
+
return response.text();
|
|
130
|
+
};
|
|
131
|
+
const apply = (name, key, outcome) => { next.sources[name] = outcome.status; if (outcome.data) next[key] = outcome.data; };
|
|
132
|
+
|
|
133
|
+
apply('openrouter', 'pricing', await fromSource(previous, 'openrouter', OPENROUTER_URL, async () => parseOpenRouter(await getJson(OPENROUTER_URL)), data => Object.keys(data).length));
|
|
134
|
+
apply('artificialAnalysis', 'codingIndex', await fromSource(previous, 'artificialAnalysis', AA_URL, async () => {
|
|
135
|
+
const key = secret(['DEVELOPER_ARTIFICIAL_ANALYSIS_API_KEY', 'ARTIFICIAL_ANALYSIS_API_KEY']);
|
|
136
|
+
if (!key) throw new Error('No Artificial Analysis key in the auth store');
|
|
137
|
+
const rows = {};
|
|
138
|
+
for (let page = 1, more = true; more && page <= 20; page += 1) {
|
|
139
|
+
const json = await getJson(`${AA_URL}?page=${page}`, { 'x-api-key': key });
|
|
140
|
+
Object.assign(rows, parseArtificialAnalysis(json));
|
|
141
|
+
more = json.pagination?.has_more === true;
|
|
142
|
+
}
|
|
143
|
+
return rows;
|
|
144
|
+
}, data => Object.keys(data).length));
|
|
145
|
+
next.sources.artificialAnalysis.attribution = 'Data from Artificial Analysis (artificialanalysis.ai)';
|
|
146
|
+
|
|
147
|
+
const swe = await fromSource(previous, 'sweBenchPro', SWE_PRO_URL, async () => parseSweBenchPro(await getText(SWE_PRO_URL)), rows => rows.length);
|
|
148
|
+
next.sources.sweBenchPro = swe.status;
|
|
149
|
+
const terminal = await fromSource(previous, 'terminalBench', TBENCH_URL, async () => parseTerminalBench(await getText(TBENCH_URL)), parsed => parsed.rows.length);
|
|
150
|
+
next.sources.terminalBench = { ...terminal.status, ...(terminal.data ? { benchmark: terminal.data.benchmark } : {}) };
|
|
151
|
+
next.leaderboards = { sweBenchPro: swe.data || previous.leaderboards?.sweBenchPro || [], terminalBench: terminal.data?.rows || previous.leaderboards?.terminalBench || [] };
|
|
152
|
+
|
|
153
|
+
const { models, errors } = harnessModelList(run);
|
|
154
|
+
next.harnessModels = { ...previous.harnessModels, ...models };
|
|
155
|
+
const failures = Object.entries(errors).map(([harness, error]) => `${harness}: ${error}`).join('; ');
|
|
156
|
+
next.sources.harnesses = { ok: !failures, fetchedAt: new Date().toISOString(), count: Object.values(next.harnessModels).flat().length, ...(failures ? { error: failures } : {}) };
|
|
157
|
+
next.updatedAt = new Date().toISOString();
|
|
158
|
+
next.source = 'sync';
|
|
159
|
+
saveCatalog(next);
|
|
160
|
+
const failed = Object.entries(next.sources).filter(([, status]) => !status.ok).map(([name, status]) => ({ source: name, error: status.error }));
|
|
161
|
+
return { ok: failed.length === 0, sources: next.sources, failed };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Attach only figures whose normalized names match. Nothing is estimated for a model without a match. */
|
|
165
|
+
export function joinHarnessModels(catalog = getModelCatalog()) {
|
|
166
|
+
const rows = [];
|
|
167
|
+
// Boards list a model once per effort or agent, best first, so the first row is its best result.
|
|
168
|
+
const board = list => {
|
|
169
|
+
const rows = new Map();
|
|
170
|
+
for (const row of list || []) { const key = normalizeModelName(row.model); if (!rows.has(key)) rows.set(key, row); }
|
|
171
|
+
return rows;
|
|
172
|
+
};
|
|
173
|
+
const swe = board(catalog.leaderboards?.sweBenchPro), terminal = board(catalog.leaderboards?.terminalBench);
|
|
174
|
+
// A board may drop the vendor ("Opus 5.5" for claude-opus-5-5). A suffix of at least five characters still names one model.
|
|
175
|
+
const lookup = (rows, key) => rows.get(key) || [...rows].find(([name]) => name.length >= 5 && key.endsWith(name))?.[1] || null;
|
|
176
|
+
for (const [harness, models] of Object.entries(catalog.harnessModels || {})) for (const model of models) {
|
|
177
|
+
const key = normalizeModelName(model.id);
|
|
178
|
+
const pricing = catalog.pricing?.[key] || null, index = catalog.codingIndex?.[key] || null;
|
|
179
|
+
rows.push({ harness, id: model.id, name: model.name, pricing, intelligenceIndex: index?.intelligenceIndex ?? null, engineeringIndex: index?.engineeringIndex ?? null, codingIndex: index?.codingIndex ?? null, agenticIndex: index?.agenticIndex ?? null, sweBenchPro: lookup(swe, key)?.score ?? null, terminalBench: lookup(terminal, key)?.score ?? null });
|
|
180
|
+
}
|
|
181
|
+
return rows;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function saveCatalog(catalog) {
|
|
185
|
+
fs.mkdirSync(path.dirname(catalogFile()), { recursive: true });
|
|
186
|
+
const temporary = `${catalogFile()}.${process.pid}.tmp`;
|
|
187
|
+
fs.writeFileSync(temporary, JSON.stringify(catalog, null, 2));
|
|
188
|
+
fs.renameSync(temporary, catalogFile());
|
|
189
|
+
const cell = value => value ?? '-';
|
|
190
|
+
const lines = ['# Fleet Model Catalog', '', `Updated: ${catalog.updatedAt}`, '', '## Sources', ''];
|
|
191
|
+
for (const [name, status] of Object.entries(catalog.sources)) lines.push(`- **${name}**: ${status.ok ? `ok, ${status.count} rows, ${status.fetchedAt}` : `failed: ${status.error}`}${status.url ? ` (${status.url})` : ''}${status.attribution ? `. ${status.attribution}` : ''}`);
|
|
192
|
+
lines.push('', '## Harness models', '', '| Harness | Model | Input / Output per 1M | Intelligence index | Engineering index | SWE-bench Pro | Terminal-Bench |', '| :--- | :--- | :--- | :--- | :--- | :--- | :--- |');
|
|
193
|
+
for (const row of joinHarnessModels(catalog)) lines.push(`| ${row.harness} | \`${row.id}\` | ${row.pricing ? `$${row.pricing.promptPer1M} / $${row.pricing.completionPer1M}` : '-'} | ${cell(row.intelligenceIndex)} | ${cell(row.engineeringIndex)} | ${cell(row.sweBenchPro)} | ${cell(row.terminalBench)} |`);
|
|
194
|
+
fs.writeFileSync(path.join(path.dirname(catalogFile()), 'MODELS.md'), lines.join('\n') + '\n');
|
|
195
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Every persistent path resolves here, at call time, so an isolated environment reaches no live store. */
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export function brainDir(env = process.env) {
|
|
6
|
+
return env.TR_SECRETS_BRAIN || path.join(env.AGENT_DIR || path.join(env.HOME || os.homedir(), '.agent'), 'skills/total-recall');
|
|
7
|
+
}
|
|
8
|
+
export const configDir = (env = process.env) => path.join(brainDir(env), 'config', 'meta-harness');
|
|
9
|
+
export const derivedDir = (env = process.env) => path.join(brainDir(env), 'memory-derived', 'meta-harness');
|
|
10
|
+
export const threadsFile = (env = process.env) => path.join(brainDir(env), 'config', 'meta-harness-threads.json');
|
|
11
|
+
export const receiptsFile = (env = process.env) => path.join(configDir(env), 'runs.jsonl');
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** The harnesses that can actually take work right now, and why the others cannot. */
|
|
2
|
+
import { ADAPTER_CATALOG } from '../capability/index.mjs';
|
|
3
|
+
import { inspectAgents, getActiveAdapters } from './worker.mjs';
|
|
4
|
+
import { readReceipts, activeQuotaWalls, activeCredentialBlocks } from './learner.mjs';
|
|
5
|
+
import { measuredWalls } from './telemetry.mjs';
|
|
6
|
+
|
|
7
|
+
const describe = (id, extra = {}) => {
|
|
8
|
+
const cap = ADAPTER_CATALOG[id] || {};
|
|
9
|
+
const spec = getActiveAdapters().find(a => a.id === id) || {};
|
|
10
|
+
return { id, name: cap.name || spec.name || id, family: cap.family || spec.family || 'custom', tier: cap.tier || spec.tier || 'budget', qualityScore: cap.qualityScore ?? spec.qualityScore ?? 6, costScore: cap.costScore ?? spec.costScore ?? 4, ...extra };
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function exclusionReason(agent, wall, credentialBlock) {
|
|
14
|
+
if (!agent.installed) return 'not installed';
|
|
15
|
+
if (!agent.versionVerified) return 'did not report a version';
|
|
16
|
+
if (agent.authenticated === false) return 'not signed in';
|
|
17
|
+
if (agent.blockedBy === 'no-chat-model') return 'no chat model installed';
|
|
18
|
+
if (wall) return `out of quota until ${wall.until}`;
|
|
19
|
+
if (credentialBlock) return `missing credential: ${String(credentialBlock.message || '').slice(0, 160)}`;
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** An explicit list is the caller's decision and is not filtered. */
|
|
24
|
+
export async function resolvePool({ availableHarnesses, inspect = inspectAgents, receipts = readReceipts } = {}) {
|
|
25
|
+
if (Array.isArray(availableHarnesses) && availableHarnesses.length) return { ready: availableHarnesses.map(id => describe(id)), excluded: [], walls: {} };
|
|
26
|
+
const [{ agents = [] }, entries] = await Promise.all([inspect({ env: process.env }), receipts()]);
|
|
27
|
+
const walls = { ...activeQuotaWalls(entries), ...measuredWalls() };
|
|
28
|
+
const blocks = activeCredentialBlocks(entries);
|
|
29
|
+
const ready = [], excluded = [];
|
|
30
|
+
for (const agent of agents) {
|
|
31
|
+
const reason = exclusionReason(agent, walls[agent.id], blocks[agent.id]);
|
|
32
|
+
if (!reason) ready.push(describe(agent.id, { authVerified: agent.authenticated === true }));
|
|
33
|
+
else if (agent.installed) excluded.push({ harness: agent.id, reason });
|
|
34
|
+
}
|
|
35
|
+
return { ready, excluded, walls };
|
|
36
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meta Harness Universal Cross-Model Adversarial Code Review Engine
|
|
3
|
+
*
|
|
4
|
+
* Runs automated code reviews across integrated agent platforms (claude, codex, agy,
|
|
5
|
+
* grok, dsh, hermes, etc.).
|
|
6
|
+
*
|
|
7
|
+
* Golden Rule: The Reviewer MUST belong to a distinct model family from the Author / Implementer
|
|
8
|
+
* to eliminate self-confirmation bias.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
12
|
+
import { ADAPTER_CATALOG } from '../capability/index.mjs';
|
|
13
|
+
import { appendThreadTurn, updateThread } from './threads.mjs';
|
|
14
|
+
import { notifyAlert } from './alerts.mjs';
|
|
15
|
+
import { dispatchWithReceipt } from './dispatch.mjs';
|
|
16
|
+
import { resolvePool } from './pool.mjs';
|
|
17
|
+
import { lastJsonWith } from './json-reply.mjs';
|
|
18
|
+
|
|
19
|
+
const MAX_DIFF = 30000;
|
|
20
|
+
// Generated surfaces say nothing a reviewer can act on, and they crowd out the source.
|
|
21
|
+
const GENERATED = ['.cursorrules', '.cursor', 'openwiki', 'package-lock.json', 'AGENTS.md', 'CLAUDE.md', 'GEMINI.md', 'INSTRUCTIONS.md'].map(item => `:!${item}`);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Read exactly what was asked for: an explicit target, the staged changes, or by default the
|
|
25
|
+
* branch against the default branch and then the uncommitted changes. An explicitly requested
|
|
26
|
+
* selection that turns out empty stays empty.
|
|
27
|
+
*/
|
|
28
|
+
export function extractGitDiff(options = {}) {
|
|
29
|
+
const cwd = options.cwd || process.cwd();
|
|
30
|
+
const read = (args, extra = []) => spawnSync('git', ['diff', ...extra, ...args, '--', '.', ...GENERATED], { cwd, encoding: 'utf8', timeout: 15000, maxBuffer: 64 * 1024 * 1024 });
|
|
31
|
+
const done = (args, diff, target) => {
|
|
32
|
+
const stat = read(args, ['--stat']).stdout || '';
|
|
33
|
+
const untracked = spawnSync('git', ['ls-files', '-o', '--exclude-standard'], { cwd, encoding: 'utf8', timeout: 15000 }).stdout || '';
|
|
34
|
+
return { ok: true, diff, target, stat: stat.trim(), untracked: untracked.trim().split('\n').filter(Boolean) };
|
|
35
|
+
};
|
|
36
|
+
const explicit = options.staged === true ? ['--staged'] : (options.target || options.branch) ? [options.target || options.branch] : null;
|
|
37
|
+
try {
|
|
38
|
+
if (explicit) {
|
|
39
|
+
const result = read(explicit);
|
|
40
|
+
if (result.status !== 0) return { ok: false, error: String(result.stderr || 'git diff failed').trim(), diff: '' };
|
|
41
|
+
return done(explicit, result.stdout.trim(), explicit.join(' '));
|
|
42
|
+
}
|
|
43
|
+
const branchRange = `${getDefaultBranch(cwd)}...HEAD`;
|
|
44
|
+
const branch = read([branchRange]);
|
|
45
|
+
if (branch.status === 0 && branch.stdout.trim()) return done([branchRange], branch.stdout.trim(), branchRange);
|
|
46
|
+
const working = read(['HEAD']);
|
|
47
|
+
if (working.status !== 0) return { ok: false, error: String(working.stderr || 'git diff failed').trim(), diff: '' };
|
|
48
|
+
return done(['HEAD'], working.stdout.trim(), 'HEAD');
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return { ok: false, error: error.message, diff: '' };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getDefaultBranch(cwd) {
|
|
55
|
+
try {
|
|
56
|
+
const res = spawnSync('git', ['symbolic-ref', 'refs/remotes/origin/HEAD'], { cwd, encoding: 'utf8', timeout: 3000 });
|
|
57
|
+
if (res.status === 0 && res.stdout.includes('/')) {
|
|
58
|
+
return res.stdout.trim().split('/').pop();
|
|
59
|
+
}
|
|
60
|
+
} catch {}
|
|
61
|
+
return 'main';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Assign an adversarial reviewer from an alternative model family
|
|
66
|
+
*/
|
|
67
|
+
export function selectAdversarialReviewer(authorHarness, availablePool = [], options = {}) {
|
|
68
|
+
const authorFamily = ADAPTER_CATALOG[authorHarness]?.family || null;
|
|
69
|
+
|
|
70
|
+
// Filter pool for distinct family
|
|
71
|
+
const distinctFamilyCandidates = availablePool.filter(h => {
|
|
72
|
+
const spec = ADAPTER_CATALOG[h.id] || h;
|
|
73
|
+
return spec.family && spec.family !== authorFamily && h.id !== authorHarness;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (distinctFamilyCandidates.length > 0) {
|
|
77
|
+
// Prefer higher quality frontier reviewers
|
|
78
|
+
distinctFamilyCandidates.sort((a, b) => (b.qualityScore || 8) - (a.qualityScore || 8));
|
|
79
|
+
return distinctFamilyCandidates[0];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Nobody independent is available when every remaining harness shares the author's family.
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Construct structured adversarial review prompt
|
|
88
|
+
*/
|
|
89
|
+
export function buildReviewPrompt(diff, context = {}) {
|
|
90
|
+
const task = context.task ? `Context / Task:\n${context.task}\n\n` : '';
|
|
91
|
+
const branch = context.branch ? `Branch: ${context.branch}\n` : '';
|
|
92
|
+
|
|
93
|
+
return `[ROLE: ADVERSARIAL CODE REVIEWER]
|
|
94
|
+
You are an independent, highly critical senior security and architecture engineer performing an adversarial code review.
|
|
95
|
+
You did NOT write this code. Do not rubber-stamp. Look for vulnerabilities, silent failures, regressions, anti-patterns, and test coverage gaps.
|
|
96
|
+
|
|
97
|
+
${task}${branch}${context.stat ? `Files changed:\n${context.stat}\n\n` : ''}${context.untracked?.length ? `New untracked files, not in the diff; read them too:\n${context.untracked.slice(0, 100).join('\n')}\n\n` : ''}${diff.length > MAX_DIFF ? `This diff is cut at ${MAX_DIFF} of ${diff.length} characters. Read the remainder from the repository and say so if you could not.\n` : ''}GIT DIFF TO REVIEW:
|
|
98
|
+
\`\`\`diff
|
|
99
|
+
${diff.slice(0, MAX_DIFF)}
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
Evaluate the diff against these strict criteria:
|
|
103
|
+
1. Security & Blast Radius (privilege escalation, token leakage, unhandled exceptions)
|
|
104
|
+
2. Invariant & Rule Adherence (does it break existing contracts or introduce silent side effects?)
|
|
105
|
+
3. Determinism & Test Coverage (are edge cases covered by automated tests?)
|
|
106
|
+
4. Architecture & Code Hygiene (readability, dead code, performance pitfalls)
|
|
107
|
+
|
|
108
|
+
Output your review in the following exact JSON format:
|
|
109
|
+
{
|
|
110
|
+
"verdict": "APPROVE" | "REQUEST_CHANGES" | "NEEDS_DISCUSSION",
|
|
111
|
+
"riskScore": <1-10, where 1 is trivial/clean and 10 is critical vulnerability>,
|
|
112
|
+
"summary": "<Concise 2-3 sentence executive summary>",
|
|
113
|
+
"findings": [
|
|
114
|
+
{
|
|
115
|
+
"severity": "critical" | "warning" | "suggestion",
|
|
116
|
+
"file": "<filename>",
|
|
117
|
+
"line": "<line number or approximate>",
|
|
118
|
+
"issue": "<concise description>",
|
|
119
|
+
"suggestedFix": "<concrete recommendation>"
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"invariantsChecked": ["<list of rules or invariants verified>"]
|
|
123
|
+
}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Only a stated verdict counts. Anything else needs a human, and says so. */
|
|
127
|
+
export function parseReviewVerdict(response) {
|
|
128
|
+
const text = String(response || '').trim();
|
|
129
|
+
const parsed = lastJsonWith(text, 'verdict');
|
|
130
|
+
if (parsed && ['APPROVE', 'REQUEST_CHANGES', 'NEEDS_DISCUSSION'].includes(parsed.verdict)) return { ...parsed, parsed: true, findings: Array.isArray(parsed.findings) ? parsed.findings : [] };
|
|
131
|
+
return { verdict: 'NEEDS_DISCUSSION', parsed: false, riskScore: null, summary: text.slice(0, 300) || 'The reviewer returned nothing readable.', findings: [] };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function executeCodeReview(options = {}) {
|
|
135
|
+
const cwd = options.cwd || process.cwd();
|
|
136
|
+
const author = options.author || null;
|
|
137
|
+
const diffResult = extractGitDiff({ cwd, target: options.target, staged: options.staged });
|
|
138
|
+
if (!diffResult.ok) throw new Error(`Failed to read the diff: ${diffResult.error}`);
|
|
139
|
+
if (!diffResult.diff) return { ok: true, verdict: 'NO_DIFF', target: diffResult.target, summary: `Nothing to review for ${diffResult.target}.` };
|
|
140
|
+
|
|
141
|
+
const { ready, excluded } = await resolvePool(options);
|
|
142
|
+
const reviewer = options.reviewer ? (ready.find(h => h.id === options.reviewer) || { id: options.reviewer, name: options.reviewer }) : selectAdversarialReviewer(author, ready);
|
|
143
|
+
if (!reviewer) throw new Error(`No independent reviewer can take work right now: ${excluded.map(e => `${e.harness} (${e.reason})`).join(', ') || 'no other harness available'}`);
|
|
144
|
+
|
|
145
|
+
const authorFamily = ADAPTER_CATALOG[author]?.family || null;
|
|
146
|
+
const reviewerFamily = ADAPTER_CATALOG[reviewer.id]?.family || reviewer.family || null;
|
|
147
|
+
if (author && (reviewer.id === author || (authorFamily && authorFamily === reviewerFamily))) throw new Error(`${reviewer.id} shares a model family with the author ${author}, so it cannot review this independently`);
|
|
148
|
+
const independence = author ? 'verified' : 'unverified: the author was not named';
|
|
149
|
+
const truncated = diffResult.diff.length > MAX_DIFF;
|
|
150
|
+
const result = await dispatchWithReceipt({
|
|
151
|
+
agent: reviewer.id, cwd, contextTask: options.task || `Review the code changes in ${diffResult.target}`,
|
|
152
|
+
prompt: buildReviewPrompt(diffResult.diff, { task: options.task, branch: options.branch || diffResult.target, stat: diffResult.stat, untracked: diffResult.untracked }),
|
|
153
|
+
permissions: { read: true, write: false }, allowUnknownAuth: true,
|
|
154
|
+
}, { dispatchFn: options.dispatchFn, role: 'Reviewer' });
|
|
155
|
+
if (!result.ok) return { ok: false, target: diffResult.target, reviewer: reviewer.id, error: result.error, failureKind: result.failureKind, excluded };
|
|
156
|
+
|
|
157
|
+
const review = { ...parseReviewVerdict(result.response), reviewer: reviewer.id, reviewerName: reviewer.name || reviewer.id, author, target: diffResult.target, truncated, independence, timestamp: new Date().toISOString() };
|
|
158
|
+
let threadError;
|
|
159
|
+
if (options.threadId) {
|
|
160
|
+
try {
|
|
161
|
+
appendThreadTurn(options.threadId, { role: 'reviewer', agent: reviewer.id, content: `[Code review by ${review.reviewerName}]\nVerdict: ${review.verdict}\nRisk: ${review.riskScore ?? 'not stated'}/10\n${review.summary || ''}` });
|
|
162
|
+
updateThread(options.threadId, { review, status: review.verdict === 'APPROVE' ? 'approved' : 'review_pending' });
|
|
163
|
+
} catch (error) { threadError = error.message; }
|
|
164
|
+
}
|
|
165
|
+
if (review.verdict === 'REQUEST_CHANGES' || review.riskScore >= 7) {
|
|
166
|
+
await notifyAlert({ title: `Code review: changes requested by ${review.reviewerName}`, message: `Risk ${review.riskScore ?? 'not stated'}/10 on ${diffResult.target}: ${String(review.summary || '').slice(0, 120)}`, severity: 'warning' });
|
|
167
|
+
}
|
|
168
|
+
return { ok: true, review, ...(threadError ? { threadError } : {}) };
|
|
169
|
+
}
|