total-recall-brain 3.41.3 → 3.41.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/bin/total-recall.mjs +10 -1
  2. package/frontend/dist/assets/{index-DFenQQEQ.js → index-CRzjQLu7.js} +77 -72
  3. package/frontend/dist/index.html +1 -1
  4. package/package.json +7 -5
  5. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +2 -2
  6. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -1
  7. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +1 -1
  8. package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +48 -0
  9. package/scaffold/.agent/skills/total-recall/plugins/auth/src/login-projections.mjs +42 -0
  10. package/scaffold/.agent/skills/total-recall/plugins/auth/src/mesh-secrets.mjs +18 -0
  11. package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/secrets.mjs +5 -2
  12. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-carriers.mjs +76 -0
  13. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-prune.mjs +37 -0
  14. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-sync.mjs +53 -17
  15. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +12 -12
  16. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +1 -1
  17. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +12 -12
  18. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
  19. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +23 -8
  20. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +6 -2
  21. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +23 -8
  22. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +5 -1
  23. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +23 -10
  24. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +5 -3
  25. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +1 -1
  26. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/grants.mjs +7 -3
  27. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +61 -38
  28. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +57 -2
  29. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +220 -36
  30. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/server/routes/mesh.mjs +7 -2
  31. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +65 -0
  32. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +59 -0
  33. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +534 -0
  34. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +139 -0
  35. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +2849 -0
  36. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +26 -0
  37. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +376 -0
  38. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +37 -0
  39. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adapters/dsh.mjs +446 -0
  40. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +23 -0
  41. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +398 -0
  42. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +152 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +82 -0
  44. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +90 -0
  45. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +415 -0
  46. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +36 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +35 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/tracker.mjs +133 -0
  49. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +233 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +32 -0
  51. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +845 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/openwiki/capability/BRICK.md +64 -0
  53. package/scaffold/.agent/skills/total-recall/plugins/openwiki/capability/index.mjs +250 -0
  54. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +344 -14
  55. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +2917 -140
  56. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +9 -3
  57. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +223 -5
  58. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +129 -0
  59. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +355 -0
  60. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +375 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +35 -22
  62. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +947 -323
  63. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +23 -8
  64. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +5 -1
  65. package/src/cli/config.mjs +3 -0
  66. package/src/cli/connect.mjs +12 -116
  67. package/src/cli/harness.mjs +0 -1
  68. package/src/cli/plugin/create.mjs +56 -3
  69. package/src/cli/plugin/index.mjs +15 -3
  70. package/src/cli/plugin/monetization.mjs +232 -12
  71. package/src/cli/preflight.mjs +161 -0
  72. package/src/core/cli-commands.mjs +2 -0
  73. package/src/core/context-compiler.mjs +8 -1
  74. package/src/core/default-plugins.mjs +1 -1
  75. package/src/core/meta-harness.mjs +16 -38
  76. package/src/core/plugin-dependencies.mjs +6 -1
  77. package/src/core/preflight-gate.mjs +77 -0
  78. package/src/core/preflight-receipt.mjs +123 -0
  79. package/src/core/runtime.mjs +0 -3
  80. package/src/core/surface.mjs +38 -1
@@ -0,0 +1,534 @@
1
+ /**
2
+ * Meta Harness Capability Brick
3
+ * Zero-dependency, host-free pure ES module for agent routing,
4
+ * complexity classification, adapter discovery, gate cascades, and SSSS receipts.
5
+ * Works identically in any standalone app, edge device, workstation, or cloud environment.
6
+ */
7
+
8
+ export const ADAPTER_CATALOG = {
9
+ agy: {
10
+ id: 'agy',
11
+ name: 'Google Antigravity CLI',
12
+ bin: 'agy',
13
+ family: 'google',
14
+ tier: 'frontier',
15
+ billingModel: 'subscription',
16
+ speed: 'fast',
17
+ costScore: 6,
18
+ qualityScore: 9.2,
19
+ contextLimit: 1048576,
20
+ modalities: ['text', 'code', 'image', 'audio', 'video'],
21
+ features: ['subagents', 'mcp', 'headless', 'worktrees', 'live_api'],
22
+ flags: { prompt: '-p', nonInteractive: '--mode accept-edits', planMode: '--mode plan', output: '--output-format json' }
23
+ },
24
+ claude: {
25
+ id: 'claude',
26
+ name: 'Anthropic Claude Code',
27
+ bin: 'claude',
28
+ family: 'anthropic',
29
+ tier: 'frontier',
30
+ billingModel: 'subscription',
31
+ speed: 'balanced',
32
+ costScore: 8,
33
+ qualityScore: 9.5,
34
+ contextLimit: 200000,
35
+ modalities: ['text', 'code'],
36
+ features: ['subagents', 'mcp', 'headless', 'compact', 'plan_mode'],
37
+ flags: { prompt: '-p', nonInteractive: '--permission-mode acceptEdits', planMode: '--permission-mode plan', output: '--output-format json' }
38
+ },
39
+ codex: {
40
+ id: 'codex',
41
+ name: 'OpenAI Codex CLI',
42
+ bin: 'codex',
43
+ family: 'openai',
44
+ tier: 'frontier',
45
+ billingModel: 'subscription',
46
+ speed: 'balanced',
47
+ costScore: 8,
48
+ qualityScore: 9.3,
49
+ contextLimit: 128000,
50
+ modalities: ['text', 'code'],
51
+ features: ['mcp', 'headless', 'streaming'],
52
+ flags: { prompt: 'exec', nonInteractive: '--sandbox workspace-write', planMode: '--sandbox read-only', output: '--json' }
53
+ },
54
+ grok: {
55
+ id: 'grok',
56
+ name: 'xAI Grok CLI',
57
+ bin: 'grok',
58
+ family: 'xai',
59
+ tier: 'frontier',
60
+ billingModel: 'subscription',
61
+ speed: 'fast',
62
+ costScore: 6,
63
+ qualityScore: 8.8,
64
+ contextLimit: 131072,
65
+ modalities: ['text', 'code', 'image'],
66
+ features: ['headless', 'search_live'],
67
+ flags: { prompt: '-p', nonInteractive: '--yes', output: '--output-format json' }
68
+ },
69
+ ollama: {
70
+ id: 'ollama',
71
+ name: 'Ollama Local Runtime',
72
+ bin: 'ollama',
73
+ family: 'local-open-weights',
74
+ tier: 'local',
75
+ billingModel: 'local',
76
+ speed: 'ultra-fast',
77
+ costScore: 0,
78
+ qualityScore: 7.5,
79
+ contextLimit: 32768,
80
+ modalities: ['text', 'code'],
81
+ features: ['offline', 'zero_cost', 'pipe_stdin', 'gpu_accelerated'],
82
+ flags: { prompt: 'run', nonInteractive: '', output: '' }
83
+ },
84
+ hermes: {
85
+ id: 'hermes',
86
+ name: 'Hermes Agent Runtime',
87
+ bin: 'hermes',
88
+ family: 'nous-open-weights',
89
+ tier: 'local',
90
+ billingModel: 'local',
91
+ speed: 'fast',
92
+ costScore: 1,
93
+ qualityScore: 8.2,
94
+ contextLimit: 65536,
95
+ modalities: ['text', 'code'],
96
+ features: ['gateway', 'mesh_dispatch', 'tool_calling'],
97
+ flags: { prompt: 'chat', nonInteractive: '--oneshot', output: '--format stream-json' }
98
+ },
99
+ dsh: {
100
+ id: 'dsh',
101
+ name: 'DeepSeek Harness',
102
+ bin: 'dsh',
103
+ family: 'deepseek',
104
+ tier: 'budget',
105
+ billingModel: 'metered',
106
+ speed: 'balanced',
107
+ costScore: 2,
108
+ qualityScore: 8.9,
109
+ contextLimit: 65536,
110
+ modalities: ['text', 'code'],
111
+ features: ['cost_efficient', 'deep_reasoning', 'mesh_routing'],
112
+ flags: { prompt: '-', nonInteractive: '--profile headless', output: '--json' }
113
+ },
114
+ openclaw: {
115
+ id: 'openclaw',
116
+ name: 'OpenClaw Multi-Agent Gateway',
117
+ bin: 'openclaw',
118
+ family: 'multi-provider',
119
+ tier: 'specialized',
120
+ billingModel: 'metered',
121
+ speed: 'balanced',
122
+ costScore: 5,
123
+ qualityScore: 8.4,
124
+ contextLimit: 65536,
125
+ modalities: ['text', 'code'],
126
+ features: ['multi_channel', 'skill_orchestration'],
127
+ flags: { prompt: 'agent', nonInteractive: '--message', output: '--json' }
128
+ },
129
+ aider: {
130
+ id: 'aider',
131
+ name: 'Aider Pair Programmer',
132
+ bin: 'aider',
133
+ family: 'multi-provider',
134
+ tier: 'budget',
135
+ billingModel: 'metered',
136
+ speed: 'balanced',
137
+ costScore: 4,
138
+ qualityScore: 8.7,
139
+ contextLimit: 128000,
140
+ modalities: ['text', 'code'],
141
+ features: ['git_repo_map', 'lint_auto_fix'],
142
+ flags: { prompt: '--message', nonInteractive: '--yes', output: '' }
143
+ },
144
+ goose: {
145
+ id: 'goose',
146
+ name: 'Block Goose Developer Agent',
147
+ bin: 'goose',
148
+ family: 'multi-provider',
149
+ tier: 'specialized',
150
+ billingModel: 'metered',
151
+ speed: 'balanced',
152
+ costScore: 5,
153
+ qualityScore: 8.3,
154
+ contextLimit: 65536,
155
+ modalities: ['text', 'code'],
156
+ features: ['mcp', 'tool_calling'],
157
+ flags: { prompt: 'run', nonInteractive: '--text', output: '' }
158
+ },
159
+ openhands: {
160
+ id: 'openhands',
161
+ name: 'OpenHands Autonomous SWE',
162
+ bin: 'openhands',
163
+ family: 'multi-provider',
164
+ tier: 'frontier',
165
+ billingModel: 'metered',
166
+ speed: 'deliberate',
167
+ costScore: 7,
168
+ qualityScore: 9.0,
169
+ contextLimit: 128000,
170
+ modalities: ['text', 'code'],
171
+ features: ['sandboxed_docker', 'swe_bench_optimized'],
172
+ flags: { prompt: '-t', nonInteractive: '--headless', output: '--json' }
173
+ },
174
+ cline: {
175
+ id: 'cline',
176
+ name: 'Cline Autonomous CLI',
177
+ bin: 'cline',
178
+ family: 'multi-provider',
179
+ tier: 'specialized',
180
+ billingModel: 'metered',
181
+ speed: 'balanced',
182
+ costScore: 5,
183
+ qualityScore: 8.5,
184
+ contextLimit: 128000,
185
+ modalities: ['text', 'code'],
186
+ features: ['mcp', 'terminal_integration'],
187
+ flags: { prompt: 'task', nonInteractive: '-y', output: '--json' }
188
+ },
189
+ muse: {
190
+ id: 'muse',
191
+ name: 'Meta Muse Code',
192
+ bin: 'muse',
193
+ family: 'meta',
194
+ tier: 'specialized',
195
+ billingModel: 'subscription',
196
+ speed: 'fast',
197
+ costScore: 4,
198
+ qualityScore: 8.4,
199
+ contextLimit: 128000,
200
+ modalities: ['text', 'code'],
201
+ features: ['headless', 'jsonl_events'],
202
+ flags: { prompt: 'exec', nonInteractive: '--json', output: '--json' }
203
+ },
204
+ pi: {
205
+ id: 'pi',
206
+ name: 'Pi Coding Agent',
207
+ bin: 'pi',
208
+ family: 'multi-provider',
209
+ tier: 'budget',
210
+ billingModel: 'metered',
211
+ speed: 'fast',
212
+ costScore: 3,
213
+ qualityScore: 8.3,
214
+ contextLimit: 128000,
215
+ modalities: ['text', 'code'],
216
+ features: ['minimal_tools', 'headless_json'],
217
+ flags: { prompt: '--print', nonInteractive: '--mode json', output: '--mode json' }
218
+ }
219
+ };
220
+
221
+ /**
222
+ * Validate custom or dynamically loaded adapter manifest
223
+ */
224
+ export function validateAdapterManifest(adapter) {
225
+ if (!adapter || typeof adapter !== 'object') {
226
+ return { valid: false, errors: ['Adapter manifest must be an object'] };
227
+ }
228
+ const errors = [];
229
+ if (!adapter.name || typeof adapter.name !== 'string') errors.push('Missing string field: name');
230
+ const bin = adapter.bin || adapter.binary;
231
+ if (!bin || typeof bin !== 'string') errors.push('Missing string field: bin (or binary)');
232
+ if (typeof adapter.costScore !== 'number') errors.push('Missing numeric field: costScore');
233
+ if (typeof adapter.qualityScore !== 'number') errors.push('Missing numeric field: qualityScore');
234
+
235
+ return {
236
+ valid: errors.length === 0,
237
+ errors
238
+ };
239
+ }
240
+
241
+ /**
242
+ * Deterministic Complexity Classifier (Jev RouteLLM heuristic)
243
+ * Scores prompt on: multi-file keywords, architectural decisions,
244
+ * security boundaries, math/crypto, and instruction surface length.
245
+ */
246
+ export function classifyTaskComplexity(taskDescription, options = {}) {
247
+ const text = String(taskDescription || '').toLowerCase();
248
+ let score = 0;
249
+ const factors = [];
250
+
251
+ if (/refactor|architect|redesign|migrate|framework|unif/.test(text)) {
252
+ score += 35;
253
+ factors.push('architectural-scope');
254
+ }
255
+ if (/auth|crypto|fido2|passkey|secret|credential|token|sandbox|isolation/.test(text)) {
256
+ score += 30;
257
+ factors.push('security-sensitive');
258
+ }
259
+ if (/multi-file|across (all|every)|entire (repo|fleet)|worktree/.test(text)) {
260
+ score += 25;
261
+ factors.push('broad-blast-radius');
262
+ }
263
+ if (/benchmark|telemetry|concurrency|race condition|memory leak/.test(text)) {
264
+ score += 20;
265
+ factors.push('complex-debugging');
266
+ }
267
+ if (/typo|rename|docstring|comment|formatting|readme|lint fix/.test(text)) {
268
+ score -= 30;
269
+ factors.push('low-complexity-edit');
270
+ }
271
+
272
+ const words = text.trim() ? text.trim().split(/\s+/).length : 0;
273
+ if (words > 120) {
274
+ score += 20;
275
+ factors.push('long-instruction-surface');
276
+ } else if (words < 15) {
277
+ score -= 10;
278
+ factors.push('terse-instruction');
279
+ }
280
+
281
+ if (options.preferLocal) {
282
+ score -= 40;
283
+ factors.push('prefer-local-override');
284
+ }
285
+ if (options.preferFrontier) {
286
+ score += 50;
287
+ factors.push('prefer-frontier-override');
288
+ }
289
+
290
+ const bounded = Math.max(0, Math.min(100, 50 + score));
291
+ let tier = 'budget';
292
+ if (bounded >= 70) {
293
+ tier = 'frontier';
294
+ } else if (bounded <= 35) {
295
+ tier = 'local';
296
+ }
297
+
298
+ return {
299
+ score: bounded,
300
+ tier,
301
+ factors,
302
+ isFrontierRecommended: tier === 'frontier',
303
+ isLocalRecommended: tier === 'local'
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Resolve normalized adapter specification from string ID or object
309
+ */
310
+ function resolveAdapterEntry(h, catalog = ADAPTER_CATALOG) {
311
+ if (typeof h === 'string') {
312
+ return catalog[h] || {
313
+ id: h,
314
+ bin: h,
315
+ name: h,
316
+ family: 'custom',
317
+ tier: 'budget',
318
+ qualityScore: 6.0,
319
+ costScore: 4.0
320
+ };
321
+ }
322
+ const id = h.id || h.bin || h.binary || 'custom';
323
+ const base = catalog[id] || {};
324
+ return {
325
+ ...base,
326
+ ...h,
327
+ id,
328
+ bin: h.bin || h.binary || base.bin || id,
329
+ name: h.name || base.name || id,
330
+ family: h.family || base.family || 'custom',
331
+ tier: h.tier || base.tier || 'budget',
332
+ qualityScore: typeof h.qualityScore === 'number' ? h.qualityScore : (base.qualityScore ?? 6.0),
333
+ costScore: typeof h.costScore === 'number' ? h.costScore : (base.costScore ?? 4.0)
334
+ };
335
+ }
336
+
337
+ /**
338
+ * Select optimal harness based on complexity score, actual installed availability,
339
+ * quota headroom, and optional empirical scorecard pass rates.
340
+ */
341
+ export function selectOptimalHarness(taskDescription, availableHarnesses = [], quotaStatus = {}, options = {}) {
342
+ const catalog = options.catalog || ADAPTER_CATALOG;
343
+ const empiricalScorecard = options.empiricalScorecard || {};
344
+ const complexity = classifyTaskComplexity(taskDescription, options);
345
+
346
+ const pool = availableHarnesses.length > 0
347
+ ? availableHarnesses.map(h => resolveAdapterEntry(h, catalog))
348
+ : Object.values(catalog);
349
+
350
+ // Filter out harnesses that are explicitly reported as exhausted or rate-limited (>95% measured usage)
351
+ // or local models when host memory is critically low
352
+ const viable = pool.filter(h => {
353
+ const q = quotaStatus[h.id] || quotaStatus[h.bin];
354
+ if (!q) return true;
355
+ if (q.status === 'exhausted' || q.status === 'unavailable' || q.status === 'low-ram') return false;
356
+ if (typeof q.usagePercent === 'number' && q.usagePercent >= 95) return false;
357
+ return true;
358
+ });
359
+
360
+ const candidates = viable.length > 0 ? viable : pool;
361
+ const isFrontier = complexity.tier === 'frontier';
362
+ const isLocal = complexity.tier === 'local';
363
+
364
+ const ranked = candidates.map(h => {
365
+ let rawUtility = 0;
366
+ if (isFrontier) {
367
+ rawUtility = (h.qualityScore * 2.0) - (h.costScore * 0.2);
368
+ if (h.tier === 'frontier') rawUtility += 5.0;
369
+ } else if (isLocal) {
370
+ rawUtility = (10 - h.costScore) * 2.0 + (h.tier === 'local' ? 5.0 : 0);
371
+ } else {
372
+ rawUtility = (h.qualityScore * 1.0) - (h.costScore * 0.8);
373
+ if (h.tier === 'budget') rawUtility += 3.0;
374
+ }
375
+
376
+ // Penalize harnesses that are installed but currently unauthenticated so ready harnesses rank first
377
+ const q = quotaStatus[h.id] || quotaStatus[h.bin];
378
+ if (q && q.status === 'needs_auth') {
379
+ rawUtility -= 6.0;
380
+ }
381
+
382
+ // Factor in empirical local pass rate if at least 2 runs recorded
383
+ const emp = empiricalScorecard[h.id] || empiricalScorecard[h.bin];
384
+ if (emp && emp.total >= 2 && typeof emp.passRate === 'number') {
385
+ const bonus = ((emp.passRate - 75) / 25) * 2.0; // -6.0 to +2.0 adjustment
386
+ rawUtility += bonus;
387
+ }
388
+
389
+ return {
390
+ harness: h.id || h.bin,
391
+ name: h.name,
392
+ family: h.family || 'custom',
393
+ tier: h.tier,
394
+ utility: parseFloat(rawUtility.toFixed(3)),
395
+ complexityMatched: h.tier === complexity.tier
396
+ };
397
+ }).sort((a, b) => b.utility - a.utility);
398
+
399
+ return {
400
+ selected: ranked[0]?.harness || pool[0]?.id || pool[0]?.bin || null,
401
+ complexity,
402
+ candidates: ranked
403
+ };
404
+ }
405
+
406
+ /**
407
+ * Pure deterministic envelope builder for preflight and execution context
408
+ */
409
+ export function buildBootstrapEnvelope(task, options = {}) {
410
+ const timestamp = options.timestamp || new Date().toISOString();
411
+ const normalizedTask = String(task || '').trim();
412
+ const harness = options.harness || 'unknown';
413
+ return {
414
+ id: options.id || `envelope-${harness}-${Buffer.from(normalizedTask.slice(0, 48)).toString('hex').slice(0, 12) || 'task'}`,
415
+ task: normalizedTask,
416
+ action: options.action || 'read,edit,test',
417
+ ready: options.ready !== false,
418
+ harness,
419
+ timestamp,
420
+ contextCapsule: {
421
+ requiredRules: options.requiredRules || [],
422
+ plugins: options.plugins || [],
423
+ budget: options.budget || 17000
424
+ },
425
+ verificationReceipt: {
426
+ preflightPassed: options.preflightPassed !== false,
427
+ ackTime: timestamp
428
+ }
429
+ };
430
+ }
431
+
432
+ /**
433
+ * Evaluate deterministic code quality gate cascade (FrugalGPT concept).
434
+ * Escalates to a higher tier (or cross-family frontier model) from the caller's
435
+ * actual available pool rather than assuming hardcoded binaries.
436
+ */
437
+ export function evaluateGateCascade(currentHarness, gateResults = {}, availableHarnesses = []) {
438
+ const { testsPassed = true, linterPassed = true, typecheckPassed = true } = gateResults;
439
+ const passedAll = testsPassed && linterPassed && typecheckPassed;
440
+
441
+ if (passedAll) {
442
+ return {
443
+ escalationRequired: false,
444
+ recommendedHarness: currentHarness,
445
+ reason: 'All deterministic gates passed.'
446
+ };
447
+ }
448
+
449
+ const pool = availableHarnesses.length > 0
450
+ ? availableHarnesses.map(h => resolveAdapterEntry(h))
451
+ : Object.values(ADAPTER_CATALOG);
452
+
453
+ const currentSpec = resolveAdapterEntry(currentHarness);
454
+ const currentTier = currentSpec.tier || 'budget';
455
+ const currentFamily = currentSpec.family || 'unknown';
456
+
457
+ const byUtility = (list) => [...list].sort((a, b) => (b.qualityScore - a.qualityScore) || (a.costScore - b.costScore));
458
+
459
+ let nextSpec = null;
460
+ let reason = 'Gate failures detected; escalating to higher-capability harness.';
461
+
462
+ if (currentTier === 'local') {
463
+ const budgetPool = byUtility(pool.filter(h => h.tier === 'budget' && h.id !== currentSpec.id));
464
+ const frontierPool = byUtility(pool.filter(h => h.tier === 'frontier' && h.id !== currentSpec.id));
465
+ nextSpec = budgetPool[0] || frontierPool[0] || byUtility(pool.filter(h => h.id !== currentSpec.id))[0];
466
+ reason = `Local tier (${currentSpec.id}) failed quality gates; escalating to ${nextSpec?.tier || 'next'} tier (${nextSpec?.id || currentSpec.id}).`;
467
+ } else if (currentTier === 'budget' || currentTier === 'specialized') {
468
+ const frontierPool = byUtility(pool.filter(h => h.tier === 'frontier' && h.id !== currentSpec.id));
469
+ nextSpec = frontierPool[0] || byUtility(pool.filter(h => h.id !== currentSpec.id))[0];
470
+ reason = `${currentTier} tier (${currentSpec.id}) failed quality gates; escalating to frontier harness (${nextSpec?.id || currentSpec.id}).`;
471
+ } else {
472
+ // Already frontier: pick a frontier harness from a DIFFERENT model family to break confirmation bias
473
+ const crossFamilyFrontier = byUtility(pool.filter(h => h.tier === 'frontier' && h.id !== currentSpec.id && h.family !== currentFamily));
474
+ const anyOtherFrontier = byUtility(pool.filter(h => h.tier === 'frontier' && h.id !== currentSpec.id));
475
+ nextSpec = crossFamilyFrontier[0] || anyOtherFrontier[0] || byUtility(pool.filter(h => h.id !== currentSpec.id))[0];
476
+ reason = `Primary frontier harness (${currentSpec.id}) failed gates; cross-auditing with distinct model family (${nextSpec?.id || currentSpec.id}).`;
477
+ }
478
+
479
+ return {
480
+ escalationRequired: true,
481
+ previousHarness: currentSpec.id,
482
+ recommendedHarness: nextSpec?.id || currentSpec.id,
483
+ reason,
484
+ failures: {
485
+ tests: !testsPassed,
486
+ linter: !linterPassed,
487
+ typecheck: !typecheckPassed
488
+ }
489
+ };
490
+ }
491
+
492
+ /**
493
+ * Compute empirical scorecard from ledger runs
494
+ */
495
+ export function computeEmpiricalScorecard(historyRuns = []) {
496
+ const summary = {};
497
+ for (const run of historyRuns) {
498
+ const h = run.harness || 'unknown';
499
+ if (!summary[h]) {
500
+ summary[h] = { total: 0, passed: 0, failed: 0, totalDurationMs: 0, passRate: 0, avgDurationMs: 0 };
501
+ }
502
+ summary[h].total += 1;
503
+ if (run.success) {
504
+ summary[h].passed += 1;
505
+ } else {
506
+ summary[h].failed += 1;
507
+ }
508
+ summary[h].totalDurationMs += (run.durationMs || 0);
509
+ }
510
+
511
+ for (const h of Object.keys(summary)) {
512
+ const item = summary[h];
513
+ item.passRate = item.total > 0 ? parseFloat(((item.passed / item.total) * 100).toFixed(1)) : 0;
514
+ item.avgDurationMs = item.total > 0 ? Math.round(item.totalDurationMs / item.total) : 0;
515
+ }
516
+
517
+ return summary;
518
+ }
519
+
520
+ /**
521
+ * Meta Harness Brick Factory
522
+ */
523
+ export function createMetaHarness(customAdapters = {}) {
524
+ const mergedAdapters = { ...ADAPTER_CATALOG, ...customAdapters };
525
+ return {
526
+ catalog: mergedAdapters,
527
+ validateAdapter: (adapter) => validateAdapterManifest(adapter),
528
+ classify: (task, opts) => classifyTaskComplexity(task, opts),
529
+ select: (task, available, quotas, opts) => selectOptimalHarness(task, available, quotas, { ...opts, catalog: mergedAdapters }),
530
+ buildEnvelope: (task, opts) => buildBootstrapEnvelope(task, opts),
531
+ evaluateCascade: (harness, gates, available) => evaluateGateCascade(harness, gates, available),
532
+ scorecard: (runs) => computeEmpiricalScorecard(runs)
533
+ };
534
+ }
@@ -0,0 +1,139 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { ADAPTERS, inspectAgents, getActiveAdapters, getCustomAdaptersDirs, loadCustomAdapters } from './src/worker.mjs';
5
+ import { inspectNode, listTargets, executeCommand, executeOnNode } from './src/executor.mjs';
6
+ import { DOC_SOURCES, documentationStatus, refreshDocumentation, ensureDocumentation, maintainDocumentation, readDocumentation, searchDocumentation } from './src/documentation.mjs';
7
+ import { manageRuntime } from './src/runtime.mjs';
8
+ import { setupIntegrations } from './src/setup.mjs';
9
+ import { inspectUpstreamFleet, getAgentChangelog } from './src/tracker.mjs';
10
+ import { inspectQuotas, getBenchmarks, routeTask } from './src/router.mjs';
11
+ import { coordinateTask } from './src/coordinator.mjs';
12
+ import { recordRunReceipt, learnFromHistory } from './src/learner.mjs';
13
+
14
+ export async function run(argv = []) {
15
+ const args = argv.slice(3); const operation = args[0] || 'status';
16
+ // Help anywhere in the arguments prints usage; it must never run an operation.
17
+ if (operation === 'help' || args.some(arg => arg === '--help' || arg === '-h')) {
18
+ const manifest=JSON.parse(fs.readFileSync(new URL('./plugin.json',import.meta.url),'utf8'));
19
+ console.log('Usage: total-recall cli-agents <operation> [options]\nEvery agent receives Total Recall bootstrap before dispatch. Runtime syntax: runtime <agent> <native-command...> [--node ID].\n');
20
+ for(const command of manifest.cli.subcommands)console.log(` ${command.name} ${(command.args || []).map(arg=>arg.flag?`${arg.flag} <${arg.name}>`:`<${arg.name}>`).join(' ')} — ${command.description}`);
21
+ return;
22
+ }
23
+ let result;
24
+ if (operation === 'status') result = {...await inspectAgents(),documentation:documentationStatus()};
25
+ else if (operation === 'catalog') result = { adapters: getActiveAdapters(), permissions: ['read', 'write', 'send', 'destructive'] };
26
+ else if (operation === 'nodes') result = await listTargets();
27
+ else if (operation === 'quota') {
28
+ const index = args.indexOf('--node');
29
+ result = await inspectQuotas({ node: index >= 0 ? args[index + 1] : 'local' });
30
+ }
31
+ else if (operation === 'benchmarks') result = getBenchmarks();
32
+ else if (operation === 'route') {
33
+ const includeUninstalled = args.includes('--include-uninstalled');
34
+ const preferLocal = args.includes('--prefer-local');
35
+ const preferFrontier = args.includes('--prefer-frontier');
36
+ const task = args.slice(1).filter(a => !a.startsWith('--')).join(' ');
37
+ if (!task) throw new Error('route requires a task description');
38
+ result = await routeTask(task, { includeUninstalled, preferLocal, preferFrontier });
39
+ }
40
+ else if (operation === 'coordinate') {
41
+ const modeIdx = args.indexOf('--mode');
42
+ const mode = modeIdx >= 0 ? args[modeIdx + 1] : 'pipeline';
43
+ const execute = args.includes('--execute');
44
+ const task = args.slice(1).filter(a => !a.startsWith('--') && a !== mode).join(' ');
45
+ if (!task) throw new Error('coordinate requires a task description');
46
+ result = await coordinateTask(task, { mode, execute });
47
+ }
48
+ else if (operation === 'learn') result = learnFromHistory();
49
+ else if (operation === 'upstream') result = await inspectUpstreamFleet(getActiveAdapters());
50
+ else if (operation === 'changelog') result = getAgentChangelog(args[1] || 'agy');
51
+ else if (operation === 'adapter') {
52
+ const sub = args[1] || 'list';
53
+ if (sub === 'list') {
54
+ result = { active: getActiveAdapters(), custom: loadCustomAdapters() };
55
+ } else if (sub === 'add') {
56
+ const target = args[2];
57
+ if (!target) throw new Error('adapter add requires a JSON spec or file path');
58
+ let spec;
59
+ try {
60
+ spec = fs.existsSync(target) ? JSON.parse(fs.readFileSync(target, 'utf8')) : JSON.parse(target);
61
+ } catch {
62
+ throw new Error('Invalid adapter JSON specification');
63
+ }
64
+ if (!spec.id || !spec.binary) throw new Error('Adapter specification must define id and binary');
65
+ const dirs = getCustomAdaptersDirs();
66
+ fs.mkdirSync(dirs[0], { recursive: true });
67
+ const dest = path.join(dirs[0], `${spec.id}.json`);
68
+ fs.writeFileSync(dest, JSON.stringify(spec, null, 2), 'utf8');
69
+ result = { ok: true, added: spec.id, path: dest };
70
+ } else if (sub === 'remove') {
71
+ const id = args[2];
72
+ if (!id) throw new Error('adapter remove requires an adapter identifier');
73
+ const dirs = getCustomAdaptersDirs();
74
+ let removed = false;
75
+ for (const d of dirs) {
76
+ const dest = path.join(d, `${id}.json`);
77
+ if (fs.existsSync(dest)) {
78
+ fs.unlinkSync(dest);
79
+ removed = true;
80
+ }
81
+ }
82
+ result = { ok: removed, removed: id };
83
+ }
84
+ }
85
+ else if(operation==='docs-sources')result={sources:DOC_SOURCES};
86
+ else if(operation==='docs-read' || operation==='docs-search') {
87
+ const option=name=>{const i=args.indexOf(name);if(i<0)return undefined;if(!args[i+1] || args[i+1].startsWith('--'))throw new Error(`${name} requires a value`);return args[i+1];};
88
+ result=operation==='docs-read'?readDocumentation({agent:option('--agent'),url:option('--url')}):searchDocumentation({agent:option('--agent'),query:option('--query')});
89
+ }
90
+ else if(operation==='setup') {
91
+ const index=args.indexOf('--node');
92
+ if(index>=0 && (!args[index+1] || args[index+1].startsWith('-')))throw new Error('--node requires a recorded node');
93
+ result=await setupIntegrations({node:index>=0?args[index+1]:undefined});
94
+ }
95
+ else if (['docs-status','docs-refresh','docs-ensure','maintenance'].includes(operation)) {
96
+ const selected = args.indexOf('--agent');
97
+ if (selected>=0 && (!args[selected+1] || args[selected+1].startsWith('-'))) throw new Error('--agent requires an adapter identifier');
98
+ const options = selected>=0 ? {agent:args[selected+1]} : {};
99
+ if(operation==='maintenance') {
100
+ const setup=await setupIntegrations();
101
+ result={...await maintainDocumentation(options),setup};
102
+ } else result = operation==='docs-status' ? documentationStatus(options) : operation==='docs-ensure' ? await ensureDocumentation(options) : await refreshDocumentation(options);
103
+ if(!result.ok)process.exitCode=1;
104
+ }
105
+ else if (operation === 'runtime') {
106
+ const runtimeArgs=args.slice(1).filter(arg=>arg!=='--json'), agent=runtimeArgs.shift();
107
+ const index=runtimeArgs.indexOf('--node'); let node='local';
108
+ if(index>=0){node=runtimeArgs[index+1];if(!node || node.startsWith('-'))throw new Error('--node requires an identifier');runtimeArgs.splice(index,2);}
109
+ result = node==='local' ? await manageRuntime(agent,runtimeArgs) : await executeOnNode(node,{operation:'direct',cwd:process.cwd(),argv:['total-recall','cli-agents','runtime',agent,...runtimeArgs,'--json']});
110
+ if(!result.ok)process.exitCode=1;
111
+ }
112
+ else if(operation==='dispatch') {
113
+ if(!args.includes('--stdin'))throw new Error('Task requests must arrive through stdin');
114
+ const input=fs.readFileSync(0,'utf8');if(Buffer.byteLength(input)>65536)throw new Error('Task request too large');
115
+ const request=JSON.parse(input);
116
+ result=await executeOnNode(request.node || 'local',{...request,operation:'dispatch'});
117
+ if(!result.ok)process.exitCode=1;
118
+ }
119
+ else if (operation === 'inspect') {
120
+ const index = args.indexOf('--node');
121
+ if (index >= 0 && (!args[index + 1] || args[index + 1].startsWith('--'))) throw new Error('--node requires a recorded node identifier');
122
+ result = await inspectNode(index >= 0 ? args[index + 1] : 'local');
123
+ }
124
+ else if (operation === 'run-command') {
125
+ if (!args.includes('--stdin')) throw new Error('Supply the command request through stdin');
126
+ const input = fs.readFileSync(0, 'utf8'); if (Buffer.byteLength(input) > 65536) throw new Error('Command request is too large');
127
+ const request = JSON.parse(input);
128
+ const root = process.env.TR_PACKAGE_ROOT;
129
+ if (!root) throw new Error('Native host context required');
130
+ if (!path.isAbsolute(root)) throw new Error('Absolute native host context required');
131
+ if (JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).name !== 'total-recall-brain') throw new Error('Native host identity mismatch');
132
+ const { getPlugin } = await import(pathToFileURL(path.join(root, 'src/core/plugin-loader.mjs')));
133
+ const plugin = getPlugin(request.pluginId, request.projectRoot || process.cwd());
134
+ result = await executeCommand({ ...request, plugin });
135
+ if (!result.ok) process.exitCode = 1;
136
+ } else throw new Error('Unknown CLI-agents operation');
137
+ console.log(JSON.stringify(result));
138
+ }
139
+ export default run;