tr-plugin-meta-harness 0.0.0-stage → 0.3.0
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 +64 -2
- package/capability/BRICK.md +59 -0
- package/capability/index.mjs +534 -0
- package/cli.mjs +139 -0
- package/package.json +20 -4
- package/plugin.json +376 -0
- package/skills/meta-harness/SKILL.md +37 -0
- package/src/adapters/dsh.mjs +446 -0
- package/src/alerts.mjs +23 -0
- package/src/coordinator.mjs +398 -0
- package/src/documentation.mjs +152 -0
- package/src/executor.mjs +82 -0
- package/src/learner.mjs +90 -0
- package/src/router.mjs +415 -0
- package/src/runtime.mjs +36 -0
- package/src/setup.mjs +35 -0
- package/src/tracker.mjs +133 -0
- package/src/worker.mjs +233 -0
- package/ui/DESIGN.md +32 -0
- package/ui/card.js +845 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meta Harness Multi-Agent Coordinator
|
|
3
|
+
* Orchestrates role-specialized multi-agent execution pipelines:
|
|
4
|
+
* - Pipeline: Architect -> Implementer -> Verifier (cross-model family, worktree-isolated)
|
|
5
|
+
* - Council: Multi-model consensus voting
|
|
6
|
+
* - Race: Speculative concurrent execution
|
|
7
|
+
*
|
|
8
|
+
* Works with any user's installed harness pool:
|
|
9
|
+
* - Dynamically discovers installed harnesses and assigns distinct model families
|
|
10
|
+
* for Architect, Implementer, and Verifier so confirmation bias is avoided.
|
|
11
|
+
* - Supports plan generation (default safe inspection) and live execution (`execute: true`)
|
|
12
|
+
* via `dispatchAgent` with real git worktree isolation for the Verifier stage.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'node:fs';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import os from 'node:os';
|
|
18
|
+
import { spawnSync } from 'node:child_process';
|
|
19
|
+
import { ADAPTER_CATALOG, classifyTaskComplexity } from '../capability/index.mjs';
|
|
20
|
+
import { inspectAgents, dispatchAgent, getActiveAdapters } from './worker.mjs';
|
|
21
|
+
import { notifyAlert } from './alerts.mjs';
|
|
22
|
+
import { recordRunReceipt } from './learner.mjs';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolve installed or available harnesses with their model family metadata
|
|
26
|
+
*/
|
|
27
|
+
async function resolvePool(options = {}) {
|
|
28
|
+
const active = getActiveAdapters();
|
|
29
|
+
let installedIds = [];
|
|
30
|
+
if (Array.isArray(options.availableHarnesses) && options.availableHarnesses.length > 0) {
|
|
31
|
+
installedIds = options.availableHarnesses;
|
|
32
|
+
} else {
|
|
33
|
+
try {
|
|
34
|
+
const inspected = await inspectAgents({ env: process.env });
|
|
35
|
+
installedIds = (inspected.agents || []).filter(a => a.installed).map(a => a.id);
|
|
36
|
+
} catch {}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const candidateIds = installedIds.length > 0 ? installedIds : active.map(a => a.id);
|
|
40
|
+
return candidateIds.map(id => {
|
|
41
|
+
const cap = ADAPTER_CATALOG[id] || {};
|
|
42
|
+
const workerSpec = active.find(a => a.id === id) || {};
|
|
43
|
+
return {
|
|
44
|
+
id,
|
|
45
|
+
name: cap.name || workerSpec.name || id,
|
|
46
|
+
family: cap.family || 'custom',
|
|
47
|
+
tier: cap.tier || 'budget',
|
|
48
|
+
qualityScore: cap.qualityScore ?? 7.5,
|
|
49
|
+
costScore: cap.costScore ?? 4
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Select three role-specialized harnesses preferring distinct model families
|
|
56
|
+
*/
|
|
57
|
+
export function assignPipelineRoles(pool = [], options = {}) {
|
|
58
|
+
const sorted = [...pool].sort((a, b) => b.qualityScore - a.qualityScore);
|
|
59
|
+
const findById = (id) => sorted.find(h => h.id === id) || (ADAPTER_CATALOG[id] ? { id, ...ADAPTER_CATALOG[id] } : { id, name: id, family: 'custom' });
|
|
60
|
+
|
|
61
|
+
const architect = options.architect
|
|
62
|
+
? findById(options.architect)
|
|
63
|
+
: (sorted[0] || findById('claude'));
|
|
64
|
+
|
|
65
|
+
const implementer = options.implementer
|
|
66
|
+
? findById(options.implementer)
|
|
67
|
+
: (sorted.find(h => h.id !== architect.id) || sorted[0] || findById('agy'));
|
|
68
|
+
|
|
69
|
+
// Verifier must use a distinct model family from Implementer whenever more than 1 family is available
|
|
70
|
+
const verifier = options.verifier
|
|
71
|
+
? findById(options.verifier)
|
|
72
|
+
: (sorted.find(h => h.family !== implementer.family && h.id !== implementer.id)
|
|
73
|
+
|| sorted.find(h => h.id !== implementer.id)
|
|
74
|
+
|| implementer);
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
architect,
|
|
78
|
+
implementer,
|
|
79
|
+
verifier,
|
|
80
|
+
heterogeneousFamilies: verifier.family !== implementer.family || pool.length <= 1
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Coordinate a task across specialized roles and model families
|
|
86
|
+
*/
|
|
87
|
+
export async function coordinateTask(taskDescription, options = {}) {
|
|
88
|
+
const mode = options.mode || 'pipeline';
|
|
89
|
+
const task = String(taskDescription || '').trim();
|
|
90
|
+
if (!task) throw new Error('Task description is required for coordination');
|
|
91
|
+
|
|
92
|
+
const pool = await resolvePool(options);
|
|
93
|
+
|
|
94
|
+
if (mode === 'pipeline') {
|
|
95
|
+
return await runSpecializedPipeline(task, pool, options);
|
|
96
|
+
} else if (mode === 'council') {
|
|
97
|
+
return await runCouncilCoordination(task, pool, options);
|
|
98
|
+
} else if (mode === 'race') {
|
|
99
|
+
return await runRaceCoordination(task, pool, options);
|
|
100
|
+
} else {
|
|
101
|
+
throw new Error(`Unsupported coordination mode: ${mode}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create a temporary detached git worktree if inside a git repository
|
|
107
|
+
*/
|
|
108
|
+
function createVerifierWorktree(cwd) {
|
|
109
|
+
try {
|
|
110
|
+
const check = spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd, encoding: 'utf8', timeout: 3000 });
|
|
111
|
+
if (check.status !== 0) return null;
|
|
112
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'meta-harness-verify-'));
|
|
113
|
+
const add = spawnSync('git', ['worktree', 'add', '--detach', tmpDir], { cwd, encoding: 'utf8', timeout: 10000 });
|
|
114
|
+
if (add.status !== 0) {
|
|
115
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return tmpDir;
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function removeVerifierWorktree(cwd, worktreeDir) {
|
|
125
|
+
if (!worktreeDir) return;
|
|
126
|
+
try {
|
|
127
|
+
spawnSync('git', ['worktree', 'remove', '--force', worktreeDir], { cwd, encoding: 'utf8', timeout: 5000 });
|
|
128
|
+
} catch {}
|
|
129
|
+
try {
|
|
130
|
+
fs.rmSync(worktreeDir, { recursive: true, force: true });
|
|
131
|
+
} catch {}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Architect -> Implementer -> Verifier Pipeline
|
|
136
|
+
* Enforces heterogeneous model families to eliminate confirmation bias.
|
|
137
|
+
*/
|
|
138
|
+
async function runSpecializedPipeline(task, pool, options = {}) {
|
|
139
|
+
const startTime = Date.now();
|
|
140
|
+
const roles = assignPipelineRoles(pool, options);
|
|
141
|
+
const executeLive = options.execute === true;
|
|
142
|
+
const cwd = options.cwd || process.cwd();
|
|
143
|
+
const dispatchFn = options.dispatchFn || dispatchAgent;
|
|
144
|
+
const complexity = classifyTaskComplexity(task);
|
|
145
|
+
|
|
146
|
+
const stages = [
|
|
147
|
+
{
|
|
148
|
+
role: 'Architect',
|
|
149
|
+
harness: roles.architect.id,
|
|
150
|
+
harnessName: roles.architect.name,
|
|
151
|
+
modelFamily: roles.architect.family,
|
|
152
|
+
permissions: { read: true, write: false },
|
|
153
|
+
status: executeLive ? 'pending' : 'planned',
|
|
154
|
+
action: 'Analyze requirements, design modular contracts, and formulate verification plan.',
|
|
155
|
+
output: null
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
role: 'Implementer',
|
|
159
|
+
harness: roles.implementer.id,
|
|
160
|
+
harnessName: roles.implementer.name,
|
|
161
|
+
modelFamily: roles.implementer.family,
|
|
162
|
+
permissions: { read: true, write: true },
|
|
163
|
+
status: executeLive ? 'pending' : 'planned',
|
|
164
|
+
action: 'Author implementation files and unit tests following Architectural blueprint.',
|
|
165
|
+
output: null
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
role: 'Verifier',
|
|
169
|
+
harness: roles.verifier.id,
|
|
170
|
+
harnessName: roles.verifier.name,
|
|
171
|
+
modelFamily: roles.verifier.family,
|
|
172
|
+
permissions: { read: true, write: false },
|
|
173
|
+
worktreeIsolated: true,
|
|
174
|
+
status: executeLive ? 'pending' : 'planned',
|
|
175
|
+
action: 'Execute deterministic test gates, audit diffs, and verify invariant compliance in isolated worktree.',
|
|
176
|
+
passed: executeLive ? null : true,
|
|
177
|
+
output: null
|
|
178
|
+
}
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
let allSucceeded = true;
|
|
182
|
+
|
|
183
|
+
if (executeLive) {
|
|
184
|
+
// Stage 1: Architect (Read-Only Plan)
|
|
185
|
+
const archStart = Date.now();
|
|
186
|
+
const archRes = await dispatchFn({
|
|
187
|
+
agent: roles.architect.id,
|
|
188
|
+
cwd,
|
|
189
|
+
prompt: `[ROLE: ARCHITECT]\nProduce a concise architectural blueprint, file list, and verification steps for:\n${task}`,
|
|
190
|
+
permissions: { read: true, write: false },
|
|
191
|
+
allowUnknownAuth: options.allowUnknownAuth === true
|
|
192
|
+
});
|
|
193
|
+
stages[0].status = archRes.ok ? 'completed' : 'failed';
|
|
194
|
+
stages[0].output = archRes.response || archRes.error || null;
|
|
195
|
+
recordRunReceipt({
|
|
196
|
+
harness: roles.architect.id,
|
|
197
|
+
task: `[Architect] ${task}`,
|
|
198
|
+
complexityScore: complexity.score,
|
|
199
|
+
success: Boolean(archRes.ok),
|
|
200
|
+
durationMs: Date.now() - archStart
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (!archRes.ok) {
|
|
204
|
+
allSucceeded = false;
|
|
205
|
+
} else {
|
|
206
|
+
// Stage 2: Implementer
|
|
207
|
+
const implStart = Date.now();
|
|
208
|
+
const implRes = await dispatchFn({
|
|
209
|
+
agent: roles.implementer.id,
|
|
210
|
+
cwd,
|
|
211
|
+
prompt: `[ROLE: IMPLEMENTER]\nFollow this Architect blueprint:\n${archRes.response}\n\nImplement the requested task:\n${task}`,
|
|
212
|
+
permissions: { read: true, write: true },
|
|
213
|
+
allowUnknownAuth: options.allowUnknownAuth === true
|
|
214
|
+
});
|
|
215
|
+
stages[1].status = implRes.ok ? 'completed' : 'failed';
|
|
216
|
+
stages[1].output = implRes.response || implRes.error || null;
|
|
217
|
+
recordRunReceipt({
|
|
218
|
+
harness: roles.implementer.id,
|
|
219
|
+
task: `[Implementer] ${task}`,
|
|
220
|
+
complexityScore: complexity.score,
|
|
221
|
+
success: Boolean(implRes.ok),
|
|
222
|
+
durationMs: Date.now() - implStart
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (!implRes.ok) {
|
|
226
|
+
allSucceeded = false;
|
|
227
|
+
} else {
|
|
228
|
+
// Stage 3: Verifier (Isolated git worktree when available)
|
|
229
|
+
const verifyStart = Date.now();
|
|
230
|
+
const worktreeDir = createVerifierWorktree(cwd);
|
|
231
|
+
const verifyCwd = worktreeDir || cwd;
|
|
232
|
+
try {
|
|
233
|
+
const verRes = await dispatchFn({
|
|
234
|
+
agent: roles.verifier.id,
|
|
235
|
+
cwd: verifyCwd,
|
|
236
|
+
prompt: `[ROLE: INDEPENDENT VERIFIER]\nAudit the implementation and run verification checks for:\n${task}\n\nArchitect Blueprint:\n${archRes.response}\n\nImplementer Summary:\n${implRes.response}`,
|
|
237
|
+
permissions: { read: true, write: false },
|
|
238
|
+
allowUnknownAuth: options.allowUnknownAuth === true
|
|
239
|
+
});
|
|
240
|
+
stages[2].status = verRes.ok ? 'completed' : 'failed';
|
|
241
|
+
stages[2].passed = Boolean(verRes.ok);
|
|
242
|
+
stages[2].output = verRes.response || verRes.error || null;
|
|
243
|
+
stages[2].worktreePath = worktreeDir || null;
|
|
244
|
+
recordRunReceipt({
|
|
245
|
+
harness: roles.verifier.id,
|
|
246
|
+
task: `[Verifier] ${task}`,
|
|
247
|
+
complexityScore: complexity.score,
|
|
248
|
+
success: Boolean(verRes.ok),
|
|
249
|
+
durationMs: Date.now() - verifyStart
|
|
250
|
+
});
|
|
251
|
+
if (!verRes.ok) allSucceeded = false;
|
|
252
|
+
} finally {
|
|
253
|
+
removeVerifierWorktree(cwd, worktreeDir);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
stages[0].output = `Planned Architect stage (${roles.architect.name}) for: ${task.slice(0, 80)}`;
|
|
259
|
+
stages[1].output = `Planned Implementer stage (${roles.implementer.name}) following Architect blueprint.`;
|
|
260
|
+
stages[2].output = `Planned cross-family Verifier stage (${roles.verifier.name}, family: ${roles.verifier.family}) in isolated git worktree.`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const durationMs = Date.now() - startTime;
|
|
264
|
+
const result = {
|
|
265
|
+
ok: allSucceeded,
|
|
266
|
+
mode: 'pipeline',
|
|
267
|
+
executedLive: executeLive,
|
|
268
|
+
task,
|
|
269
|
+
stages,
|
|
270
|
+
durationMs,
|
|
271
|
+
heterogeneousFamiliesVerified: roles.heterogeneousFamilies
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
if (executeLive) {
|
|
275
|
+
await notifyAlert({
|
|
276
|
+
title: allSucceeded ? 'Meta Harness: Pipeline Completed' : 'Meta Harness: Pipeline Failed',
|
|
277
|
+
message: `Multi-agent pipeline ${allSucceeded ? 'succeeded' : 'failed'} for task: ${task.slice(0, 60)}`,
|
|
278
|
+
severity: allSucceeded ? 'info' : 'warning'
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Council Consensus (Multi-model deliberation across available harnesses)
|
|
287
|
+
*/
|
|
288
|
+
async function runCouncilCoordination(task, pool, options = {}) {
|
|
289
|
+
const councilHarnesses = options.harnesses
|
|
290
|
+
|| pool.slice(0, 3).map(h => h.id);
|
|
291
|
+
const executeLive = options.execute === true;
|
|
292
|
+
const cwd = options.cwd || process.cwd();
|
|
293
|
+
const dispatchFn = options.dispatchFn || dispatchAgent;
|
|
294
|
+
|
|
295
|
+
if (executeLive) {
|
|
296
|
+
const dispatches = await Promise.all(councilHarnesses.map(async (h) => {
|
|
297
|
+
try {
|
|
298
|
+
const res = await dispatchFn({
|
|
299
|
+
agent: h,
|
|
300
|
+
cwd,
|
|
301
|
+
prompt: `[COUNCIL DELIBERATION]\nProvide your independent engineering assessment and recommendation for:\n${task}`,
|
|
302
|
+
permissions: { read: true, write: false },
|
|
303
|
+
allowUnknownAuth: options.allowUnknownAuth === true
|
|
304
|
+
});
|
|
305
|
+
return {
|
|
306
|
+
harness: h,
|
|
307
|
+
ok: Boolean(res.ok),
|
|
308
|
+
recommendation: res.ok ? 'completed' : 'failed',
|
|
309
|
+
response: res.response || res.error || null
|
|
310
|
+
};
|
|
311
|
+
} catch (err) {
|
|
312
|
+
return {
|
|
313
|
+
harness: h,
|
|
314
|
+
ok: false,
|
|
315
|
+
recommendation: 'error',
|
|
316
|
+
response: err.message
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}));
|
|
320
|
+
|
|
321
|
+
const passedCount = dispatches.filter(d => d.ok).length;
|
|
322
|
+
return {
|
|
323
|
+
ok: passedCount > 0,
|
|
324
|
+
mode: 'council',
|
|
325
|
+
executedLive: true,
|
|
326
|
+
task,
|
|
327
|
+
council: councilHarnesses,
|
|
328
|
+
consensus: passedCount === councilHarnesses.length ? 'unanimous' : (passedCount > 0 ? 'majority' : 'failed'),
|
|
329
|
+
votes: dispatches
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const votes = councilHarnesses.map(h => ({
|
|
334
|
+
harness: h,
|
|
335
|
+
status: 'planned',
|
|
336
|
+
recommendation: 'ready',
|
|
337
|
+
reasoning: `Scheduled independent read-only deliberation on ${h}.`
|
|
338
|
+
}));
|
|
339
|
+
|
|
340
|
+
return {
|
|
341
|
+
ok: true,
|
|
342
|
+
mode: 'council',
|
|
343
|
+
executedLive: false,
|
|
344
|
+
task,
|
|
345
|
+
council: councilHarnesses,
|
|
346
|
+
consensus: 'planned',
|
|
347
|
+
votes
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Speculative Concurrent Race
|
|
353
|
+
*/
|
|
354
|
+
async function runRaceCoordination(task, pool, options = {}) {
|
|
355
|
+
const contenders = options.harnesses
|
|
356
|
+
|| pool.slice(0, 2).map(h => h.id);
|
|
357
|
+
const executeLive = options.execute === true;
|
|
358
|
+
const cwd = options.cwd || process.cwd();
|
|
359
|
+
const dispatchFn = options.dispatchFn || dispatchAgent;
|
|
360
|
+
|
|
361
|
+
if (executeLive) {
|
|
362
|
+
const results = await Promise.all(contenders.map(async (h) => {
|
|
363
|
+
const start = Date.now();
|
|
364
|
+
try {
|
|
365
|
+
const res = await dispatchFn({
|
|
366
|
+
agent: h,
|
|
367
|
+
cwd,
|
|
368
|
+
prompt: task,
|
|
369
|
+
permissions: { read: true, write: false },
|
|
370
|
+
allowUnknownAuth: options.allowUnknownAuth === true
|
|
371
|
+
});
|
|
372
|
+
return { harness: h, ok: Boolean(res.ok), durationMs: Date.now() - start, response: res.response || null };
|
|
373
|
+
} catch (err) {
|
|
374
|
+
return { harness: h, ok: false, durationMs: Date.now() - start, error: err.message };
|
|
375
|
+
}
|
|
376
|
+
}));
|
|
377
|
+
const winners = results.filter(r => r.ok).sort((a, b) => a.durationMs - b.durationMs);
|
|
378
|
+
return {
|
|
379
|
+
ok: winners.length > 0,
|
|
380
|
+
mode: 'race',
|
|
381
|
+
executedLive: true,
|
|
382
|
+
task,
|
|
383
|
+
contenders,
|
|
384
|
+
winner: winners[0]?.harness || null,
|
|
385
|
+
results
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
ok: true,
|
|
391
|
+
mode: 'race',
|
|
392
|
+
executedLive: false,
|
|
393
|
+
task,
|
|
394
|
+
contenders,
|
|
395
|
+
winner: contenders[0] || null,
|
|
396
|
+
reason: 'Planned concurrent race across selected contenders.'
|
|
397
|
+
};
|
|
398
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/** Official documentation downloads. Cache files are disposable projections, never credentials. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
|
|
7
|
+
export const DOC_SOURCES = [
|
|
8
|
+
{ id: 'claude', index: 'https://code.claude.com/docs/llms.txt', prefix: 'https://code.claude.com/docs/', catalog:true },
|
|
9
|
+
{ id: 'codex', index: 'https://developers.openai.com/codex/llms.txt', prefix: 'https://learn.chatgpt.com/docs/', catalog:true },
|
|
10
|
+
{ id: 'agy', index: 'https://antigravity.google/docs/cli/reference', prefix: 'https://antigravity.google/docs/' },
|
|
11
|
+
{ id: 'cursor', index: 'https://cursor.com/llms.txt', prefix: 'https://cursor.com/docs', catalog:true },
|
|
12
|
+
{ id: 'dsh', repo: 'deepseek-ai/deepseek-harness', branch: 'master', directories: ['docs/', 'apps/cli/', 'packages/boot/'], index: 'https://raw.githubusercontent.com/deepseek-ai/deepseek-harness/master/apps/cli/README.md' },
|
|
13
|
+
{ id: 'grok', index: 'https://docs.x.ai/llms.txt', prefix: 'https://docs.x.ai/build/', catalog:true },
|
|
14
|
+
{ id: 'muse', index: 'https://dev.meta.ai/docs/muse-code', prefix: 'https://dev.meta.ai/docs/muse-code' },
|
|
15
|
+
{ id: 'pi', repo: 'badlogic/pi-mono', branch: 'main', directories: ['packages/coding-agent/docs/'], index: 'https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/README.md' },
|
|
16
|
+
{ id: 'hermes', repo: 'NousResearch/hermes-agent', branch: 'main', directories: ['website/docs/'], index: 'https://raw.githubusercontent.com/NousResearch/hermes-agent/main/website/docs/reference/cli-commands.md' },
|
|
17
|
+
];
|
|
18
|
+
const hash = value => crypto.createHash('sha256').update(value).digest('hex');
|
|
19
|
+
const TTL = 24 * 60 * 60 * 1000;
|
|
20
|
+
function pageFile(directory,id,file){if(!/^[a-f0-9]{64}-[a-f0-9]{64}\.txt$/.test(file || ''))throw new Error('Invalid cached documentation filename');return path.join(directory,id,file);}
|
|
21
|
+
function atomic(file, value) { fs.mkdirSync(path.dirname(file), { recursive: true }); const temp = `${file}.${process.pid}.tmp`; fs.writeFileSync(temp, value, { mode: 0o600 }); fs.renameSync(temp, file); }
|
|
22
|
+
export function documentationDirectory() {
|
|
23
|
+
const brain = process.env.TR_SECRETS_BRAIN || path.join(process.env.AGENT_DIR || path.join(os.homedir(), '.agent'), 'skills/total-recall');
|
|
24
|
+
return path.join(brain, 'memory-derived/cli-agents/docs');
|
|
25
|
+
}
|
|
26
|
+
function metadata(directory, id) { try { return JSON.parse(fs.readFileSync(path.join(directory,id,'index.json'),'utf8')); } catch { return null; } }
|
|
27
|
+
export function documentationStatus({ directory = documentationDirectory(), agent, now = Date.now() } = {}) {
|
|
28
|
+
const sources = agent ? DOC_SOURCES.filter(source => source.id === agent) : DOC_SOURCES;
|
|
29
|
+
if (!sources.length) throw new Error('Unknown documentation adapter');
|
|
30
|
+
const agents = sources.map(source => {
|
|
31
|
+
const saved = metadata(directory,source.id);
|
|
32
|
+
const complete = saved?.complete === true && saved.pages?.length > 0 && saved.pages.every(page => {
|
|
33
|
+
try { return hash(fs.readFileSync(pageFile(directory,source.id,page.file))) === page.sha256; } catch { return false; }
|
|
34
|
+
});
|
|
35
|
+
const age=now-Date.parse(saved?.checkedAt);
|
|
36
|
+
const fresh = complete && Number.isFinite(age) && age>=-300000 && age<=TTL && !saved.error;
|
|
37
|
+
return { id:source.id, complete, fresh, pages:saved?.pages?.length || 0, checkedAt:saved?.checkedAt || null, lastAttempt:saved?.lastAttempt || null, error:saved?.error || null };
|
|
38
|
+
});
|
|
39
|
+
return { ok:agents.every(row=>row.fresh), directory, agents };
|
|
40
|
+
}
|
|
41
|
+
async function download(url, fetchImpl) {
|
|
42
|
+
const allowed = new Set(['code.claude.com','claude.com','platform.claude.com','docs.anthropic.com','anthropic.com','developers.openai.com','openai.com','help.openai.com','learn.chatgpt.com','chatgpt.com','antigravity.google','cursor.com','docs.x.ai','dev.meta.ai','raw.githubusercontent.com','api.github.com']);
|
|
43
|
+
let target = new URL(url);
|
|
44
|
+
for(let redirects=0;redirects<5;redirects++) {
|
|
45
|
+
if(target.protocol!=='https:' || target.username || target.password || !allowed.has(target.hostname)) throw new Error(`Unapproved official documentation origin: ${target.hostname}`);
|
|
46
|
+
const response=await fetchImpl(target,{redirect:'manual',signal:AbortSignal.timeout(20000),headers:{'User-Agent':'Total-Recall-CLI-Agents-Docs','Accept':'*/*'}});
|
|
47
|
+
if(response.status>=300 && response.status<400){target=new URL(response.headers.get('location'),target);continue;}
|
|
48
|
+
// Some entries advertise Markdown exports which no longer exist while the
|
|
49
|
+
// same official HTML document remains available. Fetch that representation.
|
|
50
|
+
if(response.status===404 && ['cursor.com','docs.x.ai'].includes(target.hostname) && target.pathname.endsWith('.md')){target.pathname=target.pathname.slice(0,-3);continue;}
|
|
51
|
+
if(!response.ok)throw new Error(`Documentation HTTP ${response.status}: ${target.origin}${target.pathname}`);
|
|
52
|
+
const chunks=[];let size=0;
|
|
53
|
+
for await(const chunk of response.body){size+=chunk.byteLength;if(size>8*1024*1024){throw new Error('Documentation page exceeds download budget');}chunks.push(chunk);}
|
|
54
|
+
const bytes=Buffer.concat(chunks);
|
|
55
|
+
const text=new TextDecoder().decode(bytes);
|
|
56
|
+
if(text.trim().length<40)throw new Error('Empty documentation response');
|
|
57
|
+
return text;
|
|
58
|
+
}
|
|
59
|
+
throw new Error('Too many documentation redirects');
|
|
60
|
+
}
|
|
61
|
+
function links(text,base,prefix) {
|
|
62
|
+
const values=base.endsWith('llms.txt')?[...text.matchAll(/https:\/\/[^\s<>"'\])]+/g)].map(match=>match[0]):[];
|
|
63
|
+
for(const match of text.matchAll(/\]\(([^\s)]+)(?:\s+[^)]*)?\)/g)){try{values.push(new URL(match[1],base).href);}catch{}}
|
|
64
|
+
for(const match of text.matchAll(/href=["']([^"']+)["']/g)) {try{values.push(new URL(match[1],base).href);}catch{}}
|
|
65
|
+
return [...new Set(values.map(value=>{try{const url=new URL(value);url.hash='';url.search='';url.pathname=url.pathname.replace(/\/{2,}/g,'/').replace(/\/$/,'') || '/';return url.href;}catch{return '';}}))]
|
|
66
|
+
.filter(url=>url.startsWith(prefix) && !/\.(png|jpg|jpeg|svg|gif|css|js|woff2?|ico|pdf)$/i.test(url));
|
|
67
|
+
}
|
|
68
|
+
export async function refreshDocumentation({ directory = documentationDirectory(), agent, fetchImpl = fetch, now = () => new Date(), maxPages = 1500 } = {}) {
|
|
69
|
+
const sources=agent?DOC_SOURCES.filter(source=>source.id===agent):DOC_SOURCES;
|
|
70
|
+
if(!sources.length)throw new Error('Unknown documentation adapter');
|
|
71
|
+
const reports=[];
|
|
72
|
+
for(const source of sources) {
|
|
73
|
+
const previous=metadata(directory,source.id), pages=[], queue=[source.index], seen=new Set(), queued=new Set(queue), errors=[];
|
|
74
|
+
const enqueue=url=>{if(!queued.has(url)){queued.add(url);queue.push(url);}};
|
|
75
|
+
try {
|
|
76
|
+
if(source.repo) {
|
|
77
|
+
const tree=JSON.parse(await download(`https://api.github.com/repos/${source.repo}/git/trees/${source.branch}?recursive=1`,fetchImpl));
|
|
78
|
+
if(tree.truncated)throw new Error('Official repository documentation tree is truncated');
|
|
79
|
+
for(const entry of tree.tree || [])if(entry.type==='blob' && /\.(md|mdx)$/i.test(entry.path) && source.directories.some(dir=>entry.path.startsWith(dir)))enqueue(`https://raw.githubusercontent.com/${source.repo}/${source.branch}/${entry.path}`);
|
|
80
|
+
}
|
|
81
|
+
// Bounded batches; follow the official index/navigation to cover agent features.
|
|
82
|
+
while(queue.length) {
|
|
83
|
+
const batch=[];
|
|
84
|
+
while(queue.length && batch.length<4){const url=queue.shift();if(!seen.has(url)){seen.add(url);batch.push(url);}}
|
|
85
|
+
if(seen.size>maxPages)throw new Error('Official documentation exceeds configured page budget');
|
|
86
|
+
const downloads=await Promise.allSettled(batch.map(async url=>({url,text:await download(url,fetchImpl)})));
|
|
87
|
+
for(let index=0;index<downloads.length;index++) {
|
|
88
|
+
const result=downloads[index];if(result.status==='rejected'){errors.push({url:batch[index],error:result.reason.message});continue;}
|
|
89
|
+
const {url,text}=result.value;
|
|
90
|
+
const sha256=hash(text),file=`${hash(url)}-${sha256}.txt`;
|
|
91
|
+
atomic(path.join(directory,source.id,file),text);
|
|
92
|
+
pages.push({url,file,sha256,bytes:Buffer.byteLength(text)});
|
|
93
|
+
// A published llms index is the authoritative documentation catalog.
|
|
94
|
+
// HTML navigation also contains unrelated and obsolete marketing links.
|
|
95
|
+
if(source.prefix && (!source.catalog || url.endsWith('llms.txt')))for(const link of links(text,url,source.prefix))enqueue(link);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const checkedAt=now().toISOString();
|
|
99
|
+
if(errors.length){
|
|
100
|
+
const error=`${errors.length} official document(s) failed: ${errors[0].error}`;
|
|
101
|
+
const preserved=previous?.complete?previous:{id:source.id,complete:false,pages};
|
|
102
|
+
atomic(path.join(directory,source.id,'index.json'),JSON.stringify({...preserved,lastAttempt:checkedAt,error,failedCount:errors.length,failures:errors},null,2));
|
|
103
|
+
reports.push({id:source.id,ok:false,error,pages:preserved.pages?.length || 0,downloadedPages:pages.length,failedCount:errors.length,failures:errors});continue;
|
|
104
|
+
}
|
|
105
|
+
atomic(path.join(directory,source.id,'index.json'),JSON.stringify({id:source.id,complete:true,checkedAt,lastAttempt:checkedAt,pages},null,2));
|
|
106
|
+
reports.push({id:source.id,ok:true,pages:pages.length});
|
|
107
|
+
} catch(error) {
|
|
108
|
+
atomic(path.join(directory,source.id,'index.json'),JSON.stringify({...previous,id:source.id,lastAttempt:now().toISOString(),error:error.message},null,2));
|
|
109
|
+
reports.push({id:source.id,ok:false,error:error.message,pages:previous?.pages?.length || 0});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return {ok:reports.every(row=>row.ok),directory,agents:reports};
|
|
113
|
+
}
|
|
114
|
+
export async function ensureDocumentation(options={}) {
|
|
115
|
+
const status=documentationStatus(options);
|
|
116
|
+
if(status.ok)return status;
|
|
117
|
+
const refresh=[];
|
|
118
|
+
for(const row of status.agents.filter(row=>!row.fresh))refresh.push(await refreshDocumentation({...options,agent:row.id}));
|
|
119
|
+
return {...documentationStatus(options),refresh};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function maintainDocumentation(options={}) {
|
|
123
|
+
const status=documentationStatus(options);
|
|
124
|
+
const next=status.agents.filter(row=>!row.fresh).sort((a,b)=>String(a.lastAttempt || '').localeCompare(String(b.lastAttempt || '')))[0];
|
|
125
|
+
if(!next)return {...status,scope:'all-agent-documentation-current'};
|
|
126
|
+
const refreshed=await refreshDocumentation({...options,agent:next.id});
|
|
127
|
+
const current=documentationStatus(options);
|
|
128
|
+
return {...refreshed,scope:'one-stale-agent-refreshed',coverage_complete:current.ok,pending:current.agents.filter(row=>!row.fresh).map(row=>row.id)};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function readDocumentation({agent,url,directory=documentationDirectory()}) {
|
|
132
|
+
if(!DOC_SOURCES.some(source=>source.id===agent))throw new Error('Unknown documentation adapter');
|
|
133
|
+
const index=metadata(directory,agent);
|
|
134
|
+
if(!url)return {agent,pages:index?.pages?.map(page=>({url:page.url,sha256:page.sha256,bytes:page.bytes})) || []};
|
|
135
|
+
const page=index?.pages?.find(page=>page.url===url);
|
|
136
|
+
if(!page)throw new Error('Official document is not cached');
|
|
137
|
+
const content=fs.readFileSync(pageFile(directory,agent,page.file),'utf8');
|
|
138
|
+
if(hash(content)!==page.sha256)throw new Error('Cached documentation integrity mismatch');
|
|
139
|
+
return {agent,url,sha256:page.sha256,checkedAt:index.checkedAt,content};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function searchDocumentation({agent,query,directory=documentationDirectory()}) {
|
|
143
|
+
if(typeof query!=='string' || !query.trim())throw new Error('Documentation query required');
|
|
144
|
+
const sources=agent?DOC_SOURCES.filter(source=>source.id===agent):DOC_SOURCES;
|
|
145
|
+
const matches=[];
|
|
146
|
+
for(const source of sources)for(const page of metadata(directory,source.id)?.pages || []) {
|
|
147
|
+
const doc=readDocumentation({agent:source.id,url:page.url,directory});const index=doc.content.toLowerCase().indexOf(query.toLowerCase());
|
|
148
|
+
if(index>=0)matches.push({agent:source.id,url:page.url,excerpt:doc.content.slice(Math.max(0,index-120),index+600)});
|
|
149
|
+
if(matches.length>=30)return {matches,truncated:true};
|
|
150
|
+
}
|
|
151
|
+
return {matches,truncated:false};
|
|
152
|
+
}
|
package/src/executor.mjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { runWorker, runProcess, getActiveAdapters } from './worker.mjs';
|
|
5
|
+
|
|
6
|
+
async function hostModule(name) {
|
|
7
|
+
const selected = process.env.TR_PACKAGE_ROOT;
|
|
8
|
+
if (!selected || !path.isAbsolute(selected)) throw new Error('Verified native Total Recall host required');
|
|
9
|
+
const root = fs.realpathSync(selected);
|
|
10
|
+
if (JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).name !== 'total-recall-brain') throw new Error('Host package identity mismatch');
|
|
11
|
+
return import(pathToFileURL(path.join(root, 'src/core', `${name}.mjs`)).href);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_PERMISSIONS = Object.freeze({ read: true, write: false, send: false, destructive: false });
|
|
15
|
+
export function commandPermissions(value = DEFAULT_PERMISSIONS) {
|
|
16
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Command permissions must be boolean values');
|
|
17
|
+
for (const key of Object.keys(value)) if (!Object.hasOwn(DEFAULT_PERMISSIONS, key) || typeof value[key] !== 'boolean') throw new Error(`Invalid permission ${key}`);
|
|
18
|
+
return { ...DEFAULT_PERMISSIONS, ...value };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function listTargets() {
|
|
22
|
+
try {
|
|
23
|
+
const mesh = await hostModule('mesh');
|
|
24
|
+
const nodes = mesh.listEnrichedMeshNodes().map(node => ({ id: node.hostname, name: node.title || node.hostname, online: node.online, self: node.self, accessConfigured: Boolean(node.access?.ssh_user) }));
|
|
25
|
+
return {
|
|
26
|
+
nodes: nodes.length > 0 ? nodes : [{ id: 'local', name: 'Local Host', online: true, self: true, accessConfigured: true }],
|
|
27
|
+
adapters: getActiveAdapters().map(({ id, name, docs, custom }) => ({ id, name, docs, custom: Boolean(custom) }))
|
|
28
|
+
};
|
|
29
|
+
} catch {
|
|
30
|
+
return {
|
|
31
|
+
nodes: [{ id: 'local', name: 'Local Host', online: true, self: true, accessConfigured: true }],
|
|
32
|
+
adapters: getActiveAdapters().map(({ id, name, docs, custom }) => ({ id, name, docs, custom: Boolean(custom) }))
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function workerProgram(request) {
|
|
38
|
+
const source = fs.readFileSync(new URL('./worker.mjs', import.meta.url), 'utf8');
|
|
39
|
+
return source + '\nrunWorker(' + JSON.stringify(request) + ').then(result=>console.log(JSON.stringify(result))).catch(()=>{console.error("Selected agent worker failed");process.exitCode=1;});\n';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function executeOnNode(node, request, { worker = runWorker, processRunner = runProcess, mesh, access } = {}) {
|
|
43
|
+
if (!node || node === 'local') return worker(request);
|
|
44
|
+
mesh ||= await hostModule('mesh'); access ||= await hostModule('mesh-access');
|
|
45
|
+
const target = mesh.findMeshNode(node);
|
|
46
|
+
if (!target) throw new Error('Selected mesh node is not recorded');
|
|
47
|
+
if (target.self) return worker(request);
|
|
48
|
+
if (target.online === false) throw new Error('Selected mesh node is offline');
|
|
49
|
+
const resolved = access.resolveNodeAccess(target);
|
|
50
|
+
const args = access.buildSshArgs(resolved, { command: 'export PATH="$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"; exec node --input-type=module', extraOptions: ['BatchMode=yes', 'ConnectTimeout=10'] });
|
|
51
|
+
if (!args) throw new Error('Selected mesh node has no verified SSH access');
|
|
52
|
+
const result = await processRunner('ssh', args, { input: workerProgram(request), timeoutMs: request.operation === 'inspect' ? 120000 : 330000 });
|
|
53
|
+
if (result.reason || result.exitCode !== 0) throw new Error('Selected mesh worker failed; verify node access, workspace and agent installation');
|
|
54
|
+
try { return JSON.parse(result.stdout); } catch { throw new Error('Selected mesh worker returned invalid data'); }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function inspectNode(node = 'local', dependencies) {
|
|
58
|
+
return executeOnNode(node, { operation: 'inspect' }, dependencies);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function quoteArgument(value) { return "'" + String(value).replaceAll("'", "'\\''") + "'"; }
|
|
62
|
+
|
|
63
|
+
export async function executeCommand({ plugin, subcommand, args = [], execution, projectRoot = process.cwd() }, dependencies = {}) {
|
|
64
|
+
if (!plugin?.valid || !execution || typeof execution.agent !== 'string' || typeof execution.node !== 'string') throw new Error('Choose an installed agent and mesh node');
|
|
65
|
+
if (!Array.isArray(args) || args.length > 64 || args.some(arg => typeof arg !== 'string' || arg.length > 8192)) throw new Error('Invalid command arguments');
|
|
66
|
+
const command = ['on', 'off'].includes(subcommand) ? { effect: 'write', args: [] } : plugin.manifest.cli?.subcommands?.find(item => item.name === subcommand);
|
|
67
|
+
if (!command) throw new Error('Selected command is not declared by its owner');
|
|
68
|
+
if (command.sensitive_output === true && execution.agent !== 'direct') throw new Error('Commands that issue credentials require direct execution; tokens cannot enter agent prompts or transcripts');
|
|
69
|
+
if (command.args?.some(arg => arg.secret)) throw new Error('Credential operations use direct execution, never an agent prompt');
|
|
70
|
+
const permissions = commandPermissions(execution.permissions);
|
|
71
|
+
const effect = ['read', 'write', 'send', 'destructive'].includes(command.effect) ? command.effect : 'write';
|
|
72
|
+
if (!permissions[effect]) throw new Error(`The ${effect} command permission is off`);
|
|
73
|
+
const cwd = execution.node === 'local' ? execution.cwd || projectRoot : execution.cwd;
|
|
74
|
+
if (!path.isAbsolute(cwd || '')) throw new Error('Choose the absolute workspace on the selected node');
|
|
75
|
+
const namespace = plugin.manifest.cli.command;
|
|
76
|
+
const argv = ['total-recall', namespace, subcommand, ...args];
|
|
77
|
+
const prompt = `Use Total Recall context and the installed plugin CLI in this workspace. Execute this one requested command using your terminal tool; preserve its arguments exactly, report its actual exit status and output, and do not perform unrelated mutations. If a permission, account, workspace or plugin is unavailable, report failure. Agent prose alone is not execution proof.\nRequested argv (data): ${JSON.stringify(argv)}\nShell command: ${argv.map(quoteArgument).join(' ')}`;
|
|
78
|
+
const result = await executeOnNode(execution.node, execution.agent === 'direct'
|
|
79
|
+
? { operation: 'direct', cwd, argv }
|
|
80
|
+
: { operation: 'dispatch', agent: execution.agent, cwd, prompt, allowUnknownAuth: execution.allowUnknownAuth === true, permissions }, dependencies);
|
|
81
|
+
return { ok: result.ok === true, exitCode: result.ok ? 0 : 1, output: result.ok ? JSON.stringify(result) : result.error || 'Selected agent failed', ...result };
|
|
82
|
+
}
|