total-recall-brain 3.41.5 → 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.
Files changed (131) hide show
  1. package/bin/total-recall.mjs +7 -4
  2. package/frontend/dist/assets/index-4tGQfTa2.js +5605 -0
  3. package/frontend/dist/assets/index-DtwsDyFM.css +1 -0
  4. package/frontend/dist/index.html +2 -2
  5. package/frontend/package.json +11 -0
  6. package/frontend/src/App.tsx +47 -122
  7. package/frontend/src/components/plugins/plugin-frame.ts +1 -0
  8. package/frontend/src/index.css +152 -86
  9. package/frontend/src/pages/HelpPage.tsx +13 -10
  10. package/frontend/src/pages/MemoryPage.tsx +28 -30
  11. package/frontend/src/pages/NetworkPage.tsx +3 -3
  12. package/frontend/src/pages/PluginsPage.tsx +6 -5
  13. package/frontend/src/pages/RulesPage.tsx +16 -12
  14. package/package.json +3 -2
  15. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +8 -8
  16. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +3 -0
  17. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +8 -8
  18. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +3 -0
  19. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +8 -8
  20. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +3 -0
  21. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +8 -8
  22. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +3 -0
  23. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +8 -8
  24. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +4 -1
  25. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +1 -1
  26. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +136 -7
  27. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +135 -26
  28. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +8 -8
  29. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +4 -1
  30. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +228 -9
  31. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +24 -30
  32. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +29 -16
  33. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +73 -0
  34. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +182 -365
  35. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -0
  36. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +9 -3
  37. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +4 -4
  38. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/agy.mjs +187 -0
  39. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/claude.mjs +275 -0
  40. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cline.mjs +132 -0
  41. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/codex.mjs +187 -0
  42. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cursor.mjs +158 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/devin.mjs +157 -0
  44. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/dsh.mjs +183 -0
  45. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/grok.mjs +132 -0
  46. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +173 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +87 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/openclaw.mjs +173 -0
  49. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/pi.mjs +129 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate.mjs +12 -0
  51. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/json-reply.mjs +22 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +72 -70
  53. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/model-catalog.mjs +195 -0
  54. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/paths.mjs +11 -0
  55. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/pool.mjs +36 -0
  56. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +169 -0
  57. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +136 -346
  58. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +1 -1
  59. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/ssss-store.mjs +20 -0
  60. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/telemetry.mjs +43 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +156 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +117 -35
  63. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +51 -0
  64. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +398 -57
  65. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +10 -0
  66. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +3 -3
  67. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +3 -0
  68. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +42 -190
  69. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/openwiki.mjs +1 -1
  70. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/mermaid-diagrams/SKILL.md +44 -0
  71. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/write-connector/SKILL.md +56 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +8 -8
  73. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +3 -0
  74. package/src/cli/agent.mjs +12 -1
  75. package/src/cli/compact.mjs +2 -2
  76. package/src/cli/connect.mjs +56 -1
  77. package/src/cli/context.mjs +10 -7
  78. package/src/cli/doctor.mjs +5 -2
  79. package/src/cli/harness.mjs +62 -50
  80. package/src/cli/memory-entry.mjs +2 -2
  81. package/src/cli/preflight.mjs +152 -16
  82. package/src/cli/remember.mjs +19 -1
  83. package/src/cli/rules.mjs +54 -12
  84. package/src/cli/startup.mjs +1 -1
  85. package/src/cli/status.mjs +12 -2
  86. package/src/core/claude-hooks.mjs +41 -3
  87. package/src/core/cli-semantic-index.mjs +3 -2
  88. package/src/core/conflict-detector.mjs +23 -12
  89. package/src/core/context-compactor.mjs +2 -0
  90. package/src/core/context-policy.mjs +20 -0
  91. package/src/core/duplicate-guard.mjs +350 -0
  92. package/src/core/meta-harness.mjs +31 -11
  93. package/src/core/preflight-gate.mjs +2 -2
  94. package/src/core/preflight-receipt.mjs +28 -0
  95. package/src/core/scaffold-plugins.mjs +7 -0
  96. package/src/core/stdio-flush.mjs +23 -0
  97. package/src/core/surface.mjs +33 -10
  98. package/src/server/routes/plugins.mjs +2 -2
  99. package/frontend/dist/assets/index-BIQmqDKP.css +0 -1
  100. package/frontend/dist/assets/index-WT3gs-x6.js +0 -5631
  101. package/frontend/src/components/DaemonLogsTab.tsx +0 -54
  102. package/frontend/src/components/ResearchAgendaTab.tsx +0 -712
  103. package/frontend/src/components/TaskQueueTab.tsx +0 -193
  104. package/frontend/src/components/VoiceInput.tsx +0 -112
  105. package/frontend/src/pages/ChatPage.tsx +0 -1059
  106. package/frontend/src/pages/CollabPage.tsx +0 -470
  107. package/frontend/src/pages/DesignDocsPage.tsx +0 -621
  108. package/frontend/src/pages/FilesPage.tsx +0 -393
  109. package/frontend/src/pages/HealthPage.tsx +0 -359
  110. package/frontend/src/pages/InboxPage.tsx +0 -83
  111. package/frontend/src/pages/InstructionsPage.tsx +0 -583
  112. package/frontend/src/pages/IntegrationsPage.tsx +0 -761
  113. package/frontend/src/pages/MeshPage.css +0 -58
  114. package/frontend/src/pages/MeshPage.tsx +0 -1219
  115. package/frontend/src/pages/OkfPage.tsx +0 -535
  116. package/frontend/src/pages/SandboxPage.tsx +0 -113
  117. package/frontend/src/pages/SkillsPage.tsx +0 -1994
  118. package/frontend/src/pages/TasksPage.tsx +0 -427
  119. package/frontend/src/pages/UsagePage.tsx +0 -702
  120. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +0 -65
  121. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +0 -76
  122. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +0 -19
  123. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +0 -266
  124. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +0 -446
  125. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +0 -152
  126. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +0 -71
  127. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +0 -36
  128. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +0 -35
  129. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +0 -175
  130. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +0 -32
  131. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +0 -575
@@ -86,10 +86,10 @@ export const ADAPTER_CATALOG = {
86
86
  name: 'Hermes Agent Runtime',
87
87
  bin: 'hermes',
88
88
  family: 'nous-open-weights',
89
- tier: 'local',
90
- billingModel: 'local',
89
+ tier: 'budget',
90
+ billingModel: 'metered',
91
91
  speed: 'fast',
92
- costScore: 1,
92
+ costScore: 3,
93
93
  qualityScore: 8.2,
94
94
  contextLimit: 65536,
95
95
  modalities: ['text', 'code'],
@@ -341,7 +341,9 @@ function resolveAdapterEntry(h, catalog = ADAPTER_CATALOG) {
341
341
  export function selectOptimalHarness(taskDescription, availableHarnesses = [], quotaStatus = {}, options = {}) {
342
342
  const catalog = options.catalog || ADAPTER_CATALOG;
343
343
  const empiricalScorecard = options.empiricalScorecard || {};
344
- const complexity = classifyTaskComplexity(taskDescription, options);
344
+ const base = classifyTaskComplexity(taskDescription, options);
345
+ // A caller with a better classifier (such as a typed decision) may supply the tier.
346
+ const complexity = options.tierOverride ? { ...base, tier: options.tierOverride } : base;
345
347
 
346
348
  const pool = availableHarnesses.length > 0
347
349
  ? availableHarnesses.map(h => resolveAdapterEntry(h, catalog))
@@ -357,7 +359,7 @@ export function selectOptimalHarness(taskDescription, availableHarnesses = [], q
357
359
  return true;
358
360
  });
359
361
 
360
- const candidates = viable.length > 0 ? viable : pool;
362
+ const candidates = viable;
361
363
  const isFrontier = complexity.tier === 'frontier';
362
364
  const isLocal = complexity.tier === 'local';
363
365
 
@@ -378,6 +380,10 @@ export function selectOptimalHarness(taskDescription, availableHarnesses = [], q
378
380
  if (q && q.status === 'needs_auth') {
379
381
  rawUtility -= 6.0;
380
382
  }
383
+ // No login probe exists for this harness, so it ranks below one verified as signed in
384
+ if (q && q.status === 'unverified') {
385
+ rawUtility -= 1.0;
386
+ }
381
387
 
382
388
  // Factor in empirical local pass rate if at least 2 runs recorded
383
389
  const emp = empiricalScorecard[h.id] || empiricalScorecard[h.bin];
@@ -397,7 +403,7 @@ export function selectOptimalHarness(taskDescription, availableHarnesses = [], q
397
403
  }).sort((a, b) => b.utility - a.utility);
398
404
 
399
405
  return {
400
- selected: ranked[0]?.harness || pool[0]?.id || pool[0]?.bin || null,
406
+ selected: ranked[0]?.harness || null,
401
407
  complexity,
402
408
  candidates: ranked
403
409
  };
@@ -495,6 +501,8 @@ export function evaluateGateCascade(currentHarness, gateResults = {}, availableH
495
501
  export function computeEmpiricalScorecard(historyRuns = []) {
496
502
  const summary = {};
497
503
  for (const run of historyRuns) {
504
+ // Quota walls, sign-in gaps and process failures say nothing about the harness's work.
505
+ if (['quota', 'auth', 'process', 'not-ready'].includes(run.failureKind)) continue;
498
506
  const h = run.harness || 'unknown';
499
507
  if (!summary[h]) {
500
508
  summary[h] = { total: 0, passed: 0, failed: 0, totalDurationMs: 0, passRate: 0, avgDurationMs: 0 };
@@ -517,6 +525,125 @@ export function computeEmpiricalScorecard(historyRuns = []) {
517
525
  return summary;
518
526
  }
519
527
 
528
+ /**
529
+ * Synthesize raw subagent output through a strict "One-Way Valve" filter.
530
+ * Compresses verbose exploratory dumps, file trees, and search outputs into
531
+ * a dense, structured context digest bounded by maxChars (default 2000 chars, ~500 tokens).
532
+ * Prevents downstream context rot across multi-step orchestration.
533
+ */
534
+ export function synthesizeOneWayValve(rawOutput, options = {}) {
535
+ const maxChars = options.maxChars || 2000;
536
+ const text = String(rawOutput || '').trim();
537
+ if (!text) return 'No findings reported.';
538
+
539
+ // Extract file paths, line references, and key findings
540
+ const fileLines = text.split('\n');
541
+ const filesFound = new Set();
542
+ const keyObservations = [];
543
+
544
+ for (const line of fileLines) {
545
+ const trimmed = line.trim();
546
+ if (!trimmed) continue;
547
+ // Match potential file paths: e.g. src/foo.js, /path/to/file.mjs:42
548
+ const pathMatch = trimmed.match(/(?:[a-zA-Z0-9_-]+\/)+[a-zA-Z0-9._-]+\.[a-zA-Z0-9]+/);
549
+ if (pathMatch) {
550
+ filesFound.add(pathMatch[0]);
551
+ }
552
+ if (/^(found|note|warning|error|exports|function|class|symbol|defined|status|schema|todo|fixme)/i.test(trimmed) || trimmed.startsWith('- ') || trimmed.startsWith('* ')) {
553
+ if (keyObservations.length < 15) {
554
+ keyObservations.push(trimmed.replace(/^[-*]\s*/, ''));
555
+ }
556
+ }
557
+ }
558
+
559
+ const parts = [];
560
+ if (filesFound.size > 0) {
561
+ parts.push(`Relevant Files: ${Array.from(filesFound).slice(0, 10).join(', ')}`);
562
+ }
563
+ if (keyObservations.length > 0) {
564
+ parts.push(`Key Findings:\n- ${keyObservations.join('\n- ')}`);
565
+ }
566
+
567
+ let digest = parts.join('\n\n');
568
+ if (!digest || digest.length < 50) {
569
+ digest = text.length > maxChars ? `${text.slice(0, maxChars - 20)}... [truncated]` : text;
570
+ } else if (digest.length > maxChars) {
571
+ digest = `${digest.slice(0, maxChars - 20)}... [truncated]`;
572
+ }
573
+
574
+ return digest;
575
+ }
576
+
577
+ /**
578
+ * Decompose a multi-step task into specialized subagent stages:
579
+ * 1. Explorer (read-only codebase sweep, zero-cost / local preference)
580
+ * 2. Architect (modular design & file contracts, frontier preference)
581
+ * 3. Implementer (code authoring & unit test generation)
582
+ * 4. Verifier (adversarial code audit in isolated worktree, distinct model family)
583
+ */
584
+ export function decomposeTask(taskDescription, options = {}) {
585
+ const task = String(taskDescription || '').trim();
586
+ if (!task) throw new Error('Task description is required for decomposition');
587
+
588
+ const complexity = classifyTaskComplexity(task, options);
589
+ const catalog = options.catalog || ADAPTER_CATALOG;
590
+ const available = options.availableHarnesses?.length ? options.availableHarnesses : Object.keys(catalog);
591
+ const entry = id => catalog[id] || {};
592
+ const byQuality = [...available].sort((a, b) => (entry(b).qualityScore ?? 0) - (entry(a).qualityScore ?? 0));
593
+ const byCost = [...available].sort((a, b) => (entry(a).costScore ?? 99) - (entry(b).costScore ?? 99));
594
+
595
+ // Explorer takes the cheapest harness, Architect the strongest
596
+ const explorerHarness = byCost[0] || null;
597
+ const architectHarness = byQuality[0] || null;
598
+ const implementerHarness = byQuality.find(id => id !== architectHarness && id !== explorerHarness) || architectHarness;
599
+
600
+ // Verifier must come from a different model family. Without one, it stays null rather than grading its own work.
601
+ const verifierHarness = byQuality.find(id => entry(id).family && entry(id).family !== entry(implementerHarness).family) || null;
602
+ const tierOf = id => entry(id).tier || null;
603
+
604
+ return {
605
+ task,
606
+ complexity,
607
+ crossFamilyVerification: Boolean(verifierHarness),
608
+ stages: [
609
+ {
610
+ id: 'stage-1-explore',
611
+ role: 'Explorer',
612
+ preferredHarness: explorerHarness,
613
+ tier: tierOf(explorerHarness),
614
+ permissions: { read: true, write: false },
615
+ objective: 'Sweep workspace, locate relevant files, inspect interfaces, and read official documentation without context bloat.',
616
+ valveFilter: true
617
+ },
618
+ {
619
+ id: 'stage-2-architect',
620
+ role: 'Architect',
621
+ preferredHarness: architectHarness,
622
+ tier: tierOf(architectHarness),
623
+ permissions: { read: true, write: false },
624
+ objective: 'Synthesize Explorer digest into modular architectural blueprint, interface contracts, and verification gates.'
625
+ },
626
+ {
627
+ id: 'stage-3-implement',
628
+ role: 'Implementer',
629
+ preferredHarness: implementerHarness,
630
+ tier: tierOf(implementerHarness),
631
+ permissions: { read: true, write: true },
632
+ objective: 'Author production implementation files and test cases strictly following the Architectural blueprint.'
633
+ },
634
+ {
635
+ id: 'stage-4-verify',
636
+ role: 'Verifier',
637
+ preferredHarness: verifierHarness,
638
+ tier: tierOf(verifierHarness),
639
+ worktreeIsolated: true,
640
+ permissions: { read: true, write: false },
641
+ objective: 'Adversarial code review and quality gate verification executed in an isolated Git worktree with a distinct model family.'
642
+ }
643
+ ]
644
+ };
645
+ }
646
+
520
647
  /**
521
648
  * Meta Harness Brick Factory
522
649
  */
@@ -529,6 +656,8 @@ export function createMetaHarness(customAdapters = {}) {
529
656
  select: (task, available, quotas, opts) => selectOptimalHarness(task, available, quotas, { ...opts, catalog: mergedAdapters }),
530
657
  buildEnvelope: (task, opts) => buildBootstrapEnvelope(task, opts),
531
658
  evaluateCascade: (harness, gates, available) => evaluateGateCascade(harness, gates, available),
532
- scorecard: (runs) => computeEmpiricalScorecard(runs)
659
+ scorecard: (runs) => computeEmpiricalScorecard(runs),
660
+ decompose: (task, opts) => decomposeTask(task, { ...opts, catalog: mergedAdapters }),
661
+ valve: (raw, opts) => synthesizeOneWayValve(raw, opts)
533
662
  };
534
663
  }
@@ -1,7 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { spawn } from 'node:child_process';
3
4
  import { pathToFileURL } from 'node:url';
4
- import { ADAPTERS, inspectAgents, getActiveAdapters, getCustomAdaptersDirs, loadCustomAdapters } from './src/worker.mjs';
5
+ import { configDir } from './src/paths.mjs';
6
+ import { inspectAgents, getActiveAdapters, getCustomAdaptersDirs, loadCustomAdapters } from './src/worker.mjs';
5
7
  import { inspectNode, listTargets, executeCommand, executeOnNode } from './src/executor.mjs';
6
8
  import { DOC_SOURCES, documentationStatus, refreshDocumentation, ensureDocumentation, maintainDocumentation, readDocumentation, searchDocumentation } from './src/documentation.mjs';
7
9
  import { manageRuntime } from './src/runtime.mjs';
@@ -9,14 +11,62 @@ import { setupIntegrations } from './src/setup.mjs';
9
11
  import { inspectUpstreamFleet, getAgentChangelog } from './src/tracker.mjs';
10
12
  import { inspectQuotas, getBenchmarks, routeTask } from './src/router.mjs';
11
13
  import { coordinateTask } from './src/coordinator.mjs';
12
- import { recordRunReceipt, learnFromHistory } from './src/learner.mjs';
14
+ import { executeRoutedTask } from './src/cascade.mjs';
15
+ import { dispatchWithReceipt } from './src/dispatch.mjs';
16
+ import { resolvePool } from './src/pool.mjs';
17
+ import { checkAlerts, notifyAlert } from './src/alerts.mjs';
18
+ import { learnFromHistory } from './src/learner.mjs';
19
+ import { decomposeTask } from './capability/index.mjs';
20
+ import { listThreads, getThread, createThread, closeThread, deleteThread } from './src/threads.mjs';
21
+ import { executeCodeReview } from './src/review.mjs';
22
+
23
+ const VALUE_FLAGS = ['--node', '--mode', '--orchestrator', '--branch', '--reviewer', '--thread', '--status', '--platform', '--agent', '--url', '--query', '--tier', '--threshold', '--author', '--explorer', '--architect', '--implementer', '--verifier'];
24
+ export function parseArguments(args) {
25
+ const positional = [];
26
+ for (let index = 1; index < args.length; index += 1) {
27
+ if (VALUE_FLAGS.includes(args[index])) { index += 1; continue; }
28
+ if (!args[index].startsWith('--')) positional.push(args[index]);
29
+ }
30
+ const value = name => {
31
+ const index = args.indexOf(name);
32
+ if (index < 0) return undefined;
33
+ if (!args[index + 1] || args[index + 1].startsWith('--')) throw new Error(`${name} requires a value`);
34
+ return args[index + 1];
35
+ };
36
+ return { positional, value, has: name => args.includes(name) };
37
+ }
13
38
 
14
39
  export async function run(argv = []) {
40
+ try { await runOperation(argv); }
41
+ catch (error) {
42
+ const message = String(error?.message || error);
43
+ if (argv.includes('--json')) console.log(JSON.stringify({ ok: false, error: message }));
44
+ else console.error(`meta-harness: ${message}`);
45
+ process.exitCode = 1;
46
+ }
47
+ }
48
+
49
+ /** The host stops a plugin command after 10 minutes, so long runs detach and report through their thread and log. */
50
+ function detachRun(argv) {
51
+ const directory = path.join(configDir(), 'jobs');
52
+ fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
53
+ const log = path.join(directory, `${Date.now().toString(36)}.log`);
54
+ const output = fs.openSync(log, 'a', 0o600);
55
+ const entry = new URL('./cli.mjs', import.meta.url).href;
56
+ const child = spawn(process.execPath, ['--input-type=module', '-e', `import(${JSON.stringify(entry)}).then(m=>m.run(${JSON.stringify(argv.filter(arg => arg !== '--background'))}))`], { cwd: process.cwd(), detached: true, stdio: ['ignore', output, output], env: process.env });
57
+ child.unref();
58
+ fs.closeSync(output);
59
+ return { ok: true, background: true, pid: child.pid, log, follow: 'total-recall meta-harness threads list --json' };
60
+ }
61
+
62
+ async function runOperation(argv) {
15
63
  const args = argv.slice(3); const operation = args[0] || 'status';
64
+ const { positional, value, has } = parseArguments(args);
65
+ if (has('--background') && ['route', 'coordinate', 'orchestrate'].includes(operation) && has('--execute')) { console.log(JSON.stringify(detachRun(argv))); return; }
16
66
  // Help anywhere in the arguments prints usage; it must never run an operation.
17
67
  if (operation === 'help' || args.some(arg => arg === '--help' || arg === '-h')) {
18
68
  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');
69
+ console.log('Usage: total-recall meta-harness <operation> [options] (alias: cli-agents)\nEvery agent receives Total Recall bootstrap before dispatch. Runtime syntax: runtime <agent> <native-command...> [--node ID].\n');
20
70
  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
71
  return;
22
72
  }
@@ -24,28 +74,82 @@ export async function run(argv = []) {
24
74
  if (operation === 'status') result = {...await inspectAgents(),documentation:documentationStatus()};
25
75
  else if (operation === 'catalog') result = { adapters: getActiveAdapters(), permissions: ['read', 'write', 'send', 'destructive'] };
26
76
  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();
77
+ else if (operation === 'quota') result = await inspectQuotas({ node: value('--node') || 'local' });
78
+ else if (operation === 'benchmarks') result = await getBenchmarks();
32
79
  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(' ');
80
+ const task = positional.join(' ');
37
81
  if (!task) throw new Error('route requires a task description');
38
- result = await routeTask(task, { includeUninstalled, preferLocal, preferFrontier });
82
+ const options = { jev: has('--jev'), preferLocal: has('--prefer-local'), preferFrontier: has('--prefer-frontier') };
83
+ result = has('--execute') ? await executeRoutedTask(task, { ...options, gate: has('--gate'), gateOptions: { tier: value('--tier') || 'fast' } }) : await routeTask(task, options);
84
+ if (!result.ok) process.exitCode = 1;
39
85
  }
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 });
86
+ else if (operation === 'decompose') {
87
+ const task = positional.join(' ');
88
+ if (!task) throw new Error('decompose requires a task description');
89
+ const pool = await resolvePool();
90
+ result = { ...decomposeTask(task, { availableHarnesses: pool.ready.map(h => h.id) }), excluded: pool.excluded };
91
+ }
92
+ else if (operation === 'coordinate' || operation === 'orchestrate') {
93
+ const task = positional.join(' ');
94
+ if (!task) throw new Error(`${operation} requires a task description`);
95
+ result = await coordinateTask(task, {
96
+ mode: value('--mode') || 'pipeline', execute: has('--execute'), orchestrator: value('--orchestrator'), explorer: value('--explorer'), architect: value('--architect'), implementer: value('--implementer'), verifier: value('--verifier'),
97
+ branch: value('--branch'), subagents: !has('--no-subagents'), autoReview: has('--auto-review'), gate: has('--gate'), gateOptions: { tier: value('--tier') || 'fast' }, allowSameFamily: has('--allow-same-family'), reviewer: value('--reviewer'),
98
+ });
99
+ if (!result.ok) process.exitCode = 1;
100
+ }
101
+ else if (operation === 'threads') {
102
+ const sub = args[1] || 'list';
103
+ if (sub === 'list') {
104
+ const statusIdx = args.indexOf('--status');
105
+ const platformIdx = args.indexOf('--platform');
106
+ result = listThreads({
107
+ status: statusIdx >= 0 ? args[statusIdx + 1] : undefined,
108
+ platform: platformIdx >= 0 ? args[platformIdx + 1] : undefined
109
+ });
110
+ } else if (sub === 'show' || sub === 'get') {
111
+ const id = args[2];
112
+ if (!id) throw new Error('threads show requires a thread id');
113
+ result = getThread(id);
114
+ if (!result) throw new Error(`Thread not found: ${id}`);
115
+ } else if (sub === 'create') {
116
+ const title = args.slice(2).filter(a => !a.startsWith('--')).join(' ');
117
+ const platformIdx = args.indexOf('--platform');
118
+ const branchIdx = args.indexOf('--branch');
119
+ result = createThread({
120
+ title: title || 'New Agent Thread',
121
+ platform: platformIdx >= 0 ? args[platformIdx + 1] : 'universal',
122
+ branch: branchIdx >= 0 ? args[branchIdx + 1] : null
123
+ });
124
+ } else if (sub === 'close') {
125
+ const id = args[2];
126
+ if (!id) throw new Error('threads close requires a thread id');
127
+ result = closeThread(id);
128
+ } else if (sub === 'delete') {
129
+ const id = args[2];
130
+ if (!id) throw new Error('threads delete requires a thread id');
131
+ result = { ok: deleteThread(id) };
132
+ } else {
133
+ throw new Error(`Unsupported threads operation: ${sub}`);
134
+ }
135
+ }
136
+ else if (operation === 'review') {
137
+ result = await executeCodeReview({ target: positional[0] || null, reviewer: value('--reviewer'), author: value('--author'), threadId: value('--thread'), staged: has('--staged') });
138
+ if (!result.ok) process.exitCode = 1;
139
+ }
140
+ else if (operation === 'learn') {
141
+ result = await learnFromHistory({ emit: has('--emit') });
142
+ if (!result.ok) process.exitCode = 1;
143
+ }
144
+ else if (operation === 'alerts') {
145
+ const sub = positional[0] || 'check';
146
+ if (sub === 'check') {
147
+ const { quotas } = await inspectQuotas({});
148
+ result = await checkAlerts({ quotas, threshold: Number(value('--threshold')) || 85, dryRun: has('--dry-run') });
149
+ } else if (sub === 'test') result = await notifyAlert({ title: 'Meta Harness: test alert', message: 'Alert delivery check.', dryRun: has('--dry-run') });
150
+ else throw new Error(`Unsupported alerts operation: ${sub}`);
151
+ if (!result.ok) process.exitCode = 1;
47
152
  }
48
- else if (operation === 'learn') result = learnFromHistory();
49
153
  else if (operation === 'upstream') result = await inspectUpstreamFleet(getActiveAdapters());
50
154
  else if (operation === 'changelog') result = getAgentChangelog(args[1] || 'agy');
51
155
  else if (operation === 'adapter') {
@@ -97,8 +201,13 @@ export async function run(argv = []) {
97
201
  if (selected>=0 && (!args[selected+1] || args[selected+1].startsWith('-'))) throw new Error('--agent requires an adapter identifier');
98
202
  const options = selected>=0 ? {agent:args[selected+1]} : {};
99
203
  if(operation==='maintenance') {
100
- const setup=await setupIntegrations();
101
- result={...await maintainDocumentation(options),setup};
204
+ if (has('--models')) {
205
+ const { syncModelCatalog } = await import('./src/model-catalog.mjs');
206
+ result = await syncModelCatalog();
207
+ } else {
208
+ const setup=await setupIntegrations();
209
+ result={...await maintainDocumentation(options),setup};
210
+ }
102
211
  } else result = operation==='docs-status' ? documentationStatus(options) : operation==='docs-ensure' ? await ensureDocumentation(options) : await refreshDocumentation(options);
103
212
  if(!result.ok)process.exitCode=1;
104
213
  }
@@ -106,14 +215,14 @@ export async function run(argv = []) {
106
215
  const runtimeArgs=args.slice(1).filter(arg=>arg!=='--json'), agent=runtimeArgs.shift();
107
216
  const index=runtimeArgs.indexOf('--node'); let node='local';
108
217
  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']});
218
+ result = node==='local' ? await manageRuntime(agent,runtimeArgs) : await executeOnNode(node,{operation:'direct',cwd:process.cwd(),argv:['total-recall','meta-harness','runtime',agent,...runtimeArgs,'--json']});
110
219
  if(!result.ok)process.exitCode=1;
111
220
  }
112
221
  else if(operation==='dispatch') {
113
222
  if(!args.includes('--stdin'))throw new Error('Task requests must arrive through stdin');
114
223
  const input=fs.readFileSync(0,'utf8');if(Buffer.byteLength(input)>65536)throw new Error('Task request too large');
115
224
  const request=JSON.parse(input);
116
- result=await executeOnNode(request.node || 'local',{...request,operation:'dispatch'});
225
+ result=await dispatchWithReceipt({...request,allowUnknownAuth:request.allowUnknownAuth===true},{node:request.node || 'local',role:'Dispatch'});
117
226
  if(!result.ok)process.exitCode=1;
118
227
  }
119
228
  else if (operation === 'inspect') {
@@ -133,7 +242,7 @@ export async function run(argv = []) {
133
242
  const plugin = getPlugin(request.pluginId, request.projectRoot || process.cwd());
134
243
  result = await executeCommand({ ...request, plugin });
135
244
  if (!result.ok) process.exitCode = 1;
136
- } else throw new Error('Unknown CLI-agents operation');
245
+ } else throw new Error(`Unknown operation: ${operation}`);
137
246
  console.log(JSON.stringify(result));
138
247
  }
139
248
  export default run;
@@ -11,7 +11,7 @@
11
11
  "@gregiteen/ssss-cli": "0.14.4"
12
12
  },
13
13
  "devDependencies": {
14
- "total-recall-brain": "3.41.4"
14
+ "total-recall-brain": "3.41.5"
15
15
  }
16
16
  },
17
17
  "node_modules/@alcalzone/ansi-tokenize": {
@@ -1589,9 +1589,9 @@
1589
1589
  },
1590
1590
  "node_modules/@ssss/cli": {
1591
1591
  "name": "@gregiteen/ssss-cli",
1592
- "version": "0.14.3",
1593
- "resolved": "https://registry.npmjs.org/@gregiteen/ssss-cli/-/ssss-cli-0.14.3.tgz",
1594
- "integrity": "sha512-vvGgiNLjus3/xp+iotiGqbSG0R7+Q/bP5cP2Psa7Rhtm/vrMuM5B4LebYu7bPf3TzgCyTbv96PcxGWf6gxbW0Q==",
1592
+ "version": "0.14.4",
1593
+ "resolved": "https://registry.npmjs.org/@gregiteen/ssss-cli/-/ssss-cli-0.14.4.tgz",
1594
+ "integrity": "sha512-R6JIgPRvS9xXp0xb6BYO3rauv+72r35GIiDrJcvbjPdTSHFZXr8/jRxFOgoF8yTed/5xyGFF3ey3DI9bOjmHVA==",
1595
1595
  "dev": true,
1596
1596
  "license": "ISC",
1597
1597
  "bin": {
@@ -5546,9 +5546,9 @@
5546
5546
  }
5547
5547
  },
5548
5548
  "node_modules/total-recall-brain": {
5549
- "version": "3.41.4",
5550
- "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.41.4.tgz",
5551
- "integrity": "sha512-xn6Ye5R/fHT7JP/ZjNJwmpteG/jcD9dzErbYwE46ZGThVaGsP1H81avSn0ZOAuLdUFRSgsoEijiAEHSaUA/4uQ==",
5549
+ "version": "3.41.5",
5550
+ "resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.41.5.tgz",
5551
+ "integrity": "sha512-gaW46xAtnFf+zfkv8LHYQLMzt27JjelyFggc8zAZafCnCJRbNSP8D02cbbcx49f3a70t/hkgA9RhJOY8HTqRFg==",
5552
5552
  "dev": true,
5553
5553
  "license": "MIT",
5554
5554
  "workspaces": [
@@ -5556,7 +5556,7 @@
5556
5556
  ],
5557
5557
  "dependencies": {
5558
5558
  "@simplewebauthn/server": "^13.3.2",
5559
- "@ssss/cli": "npm:@gregiteen/ssss-cli@0.14.3",
5559
+ "@ssss/cli": "npm:@gregiteen/ssss-cli@0.14.4",
5560
5560
  "bcrypt": "^6.0.0",
5561
5561
  "better-sqlite3": "^12.11.1",
5562
5562
  "cookie-parser": "^1.4.7",
@@ -4,7 +4,7 @@
4
4
  "description": "Total Recall Meta Harness plugin",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "test": "node --test test/*.test.mjs",
7
+ "test": "node --import ./test/isolate.mjs --test test/*.test.mjs",
8
8
  "ssss": "ssss"
9
9
  },
10
10
  "files": [
@@ -22,5 +22,8 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@gregiteen/ssss-cli": "0.14.4"
25
+ },
26
+ "overrides": {
27
+ "@ssss/cli": "npm:@gregiteen/ssss-cli@0.14.4"
25
28
  }
26
29
  }