nexus-prime 7.0.1 → 7.1.1

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 (51) hide show
  1. package/README.md +0 -8
  2. package/dist/agents/adapters/mcp/definitions.d.ts +0 -19
  3. package/dist/agents/adapters/mcp/definitions.js +1 -99
  4. package/dist/agents/adapters/mcp/dispatch.js +0 -6
  5. package/dist/agents/adapters/mcp/handlers/governance.js +4 -4
  6. package/dist/agents/adapters/mcp/handlers/orchestration.js +16 -42
  7. package/dist/agents/adapters/mcp/helpers.d.ts +4 -2
  8. package/dist/agents/adapters/mcp/helpers.js +13 -4
  9. package/dist/architects/bootstrap.d.ts +6 -0
  10. package/dist/architects/bootstrap.js +1 -1
  11. package/dist/cli/cleanup.d.ts +25 -0
  12. package/dist/cli/cleanup.js +114 -0
  13. package/dist/cli/doctor-storage.d.ts +19 -0
  14. package/dist/cli/doctor-storage.js +78 -0
  15. package/dist/cli/install-wizard.js +31 -0
  16. package/dist/cli/reset.d.ts +8 -8
  17. package/dist/cli/reset.js +37 -68
  18. package/dist/cli/uninstall.d.ts +46 -0
  19. package/dist/cli/uninstall.js +177 -0
  20. package/dist/cli.js +61 -33
  21. package/dist/dashboard/app/views/memory.js +102 -16
  22. package/dist/dashboard/routes/architects.js +10 -2
  23. package/dist/dashboard/routes/assets.js +43 -3
  24. package/dist/dashboard/routes/governance.js +115 -2
  25. package/dist/dashboard/routes/graph.js +23 -2
  26. package/dist/dashboard/routes/health.js +2 -1
  27. package/dist/dashboard/routes/license.js +43 -4
  28. package/dist/dashboard/routes/memory.js +66 -5
  29. package/dist/dashboard/server.js +0 -13
  30. package/dist/dashboard/types.d.ts +0 -2
  31. package/dist/engines/memory/store.js +0 -2
  32. package/dist/engines/memory.js +11 -11
  33. package/dist/engines/orchestrator.d.ts +1 -1
  34. package/dist/engines/orchestrator.js +16 -31
  35. package/dist/engines/runtime-hygiene.d.ts +6 -0
  36. package/dist/engines/runtime-hygiene.js +51 -0
  37. package/dist/index.js +4 -0
  38. package/dist/install/fs-purge.d.ts +65 -0
  39. package/dist/install/fs-purge.js +304 -0
  40. package/dist/install/manifest.d.ts +36 -0
  41. package/dist/install/manifest.js +107 -0
  42. package/dist/install/process-cleanup.d.ts +21 -0
  43. package/dist/install/process-cleanup.js +161 -0
  44. package/dist/install/registration.d.ts +41 -0
  45. package/dist/install/registration.js +258 -0
  46. package/dist/install/state-locator.d.ts +32 -0
  47. package/dist/install/state-locator.js +115 -0
  48. package/dist/synapse/bootstrap.d.ts +6 -0
  49. package/dist/synapse/bootstrap.js +1 -1
  50. package/dist/synapse/config.js +5 -1
  51. package/package.json +2 -2
package/README.md CHANGED
@@ -137,8 +137,6 @@ That's it. Memory, budgeting, and safe execution are already on.
137
137
 
138
138
  Prefer one-liner installs? `curl -fsSL https://nexus-prime.cfd/install.sh | bash`
139
139
 
140
- Your agent calls `nexus_session_bootstrap` + `nexus_orchestrate` automatically. Auto-bootstrap runs on first non-bootstrap call, so you never have to wire the packet by hand.
141
-
142
140
  ---
143
141
 
144
142
  ## Features
@@ -255,12 +253,6 @@ Or just wire everything: **`nexus-prime setup all`**
255
253
 
256
254
  ---
257
255
 
258
- ## Competitive position
259
-
260
- Nexus Prime is the only control plane that remembers the repo, budgets the tokens, and ghost-passes every mutation — locally, in one process. Compare head-to-head on [nexus-prime.cfd/comparison.html](https://nexus-prime.cfd/comparison.html) or read the full docs on [nexus-prime.cfd/knowledge-base.html](https://nexus-prime.cfd/knowledge-base.html).
261
-
262
- ---
263
-
264
256
  ## What users see
265
257
 
266
258
  Numbers from real users running real workflows on their own machines:
@@ -2,24 +2,5 @@ export type McpToolDefinition = {
2
2
  name: string;
3
3
  description: string;
4
4
  inputSchema: Record<string, unknown>;
5
- /**
6
- * When true, this tool is a legacy alias. Callers should migrate to
7
- * `deprecatedReplacement`. The tool continues to work; a telemetry event
8
- * `nexus.deprecation.used` is emitted on each call.
9
- */
10
- deprecated?: true;
11
- /**
12
- * MCP tool name or CLI command that replaces this deprecated tool.
13
- * Surfaced by nexus_describe_tool.
14
- */
15
- deprecatedReplacement?: string;
16
5
  };
17
- /**
18
- * Tools in this set are first-class primitives — they are the canonical API
19
- * surface for the Nexus Prime MCP adapter. All other tools are surfaced as
20
- * deprecated (they continue to work but callers should migrate).
21
- *
22
- * PR4+5 W6: shrink surface from 93 tools to ~16 first-class + deprecated rest.
23
- */
24
- export declare const FIRST_CLASS_TOOLS: Set<string>;
25
6
  export declare function buildMcpToolDefinitions(): McpToolDefinition[];
@@ -1,39 +1,8 @@
1
1
  import { synapseToolDefinitions } from '../../../synapse/index.js';
2
2
  import { architectsToolDefinitions } from '../../../architects/index.js';
3
3
  import { getGstackBridge } from '../../../engines/gstack-bridge.js';
4
- /**
5
- * Tools in this set are first-class primitives — they are the canonical API
6
- * surface for the Nexus Prime MCP adapter. All other tools are surfaced as
7
- * deprecated (they continue to work but callers should migrate).
8
- *
9
- * PR4+5 W6: shrink surface from 93 tools to ~16 first-class + deprecated rest.
10
- */
11
- export const FIRST_CLASS_TOOLS = new Set([
12
- // Session lifecycle
13
- 'nexus_session_bootstrap',
14
- 'nexus_orchestrate',
15
- 'nexus_session_dna',
16
- // Memory
17
- 'nexus_store_memory',
18
- 'nexus_recall_memory',
19
- 'nexus_memory_stats',
20
- // Context + governance
21
- 'nexus_optimize_tokens',
22
- 'nexus_mindkit_check',
23
- 'nexus_ghost_pass',
24
- // Discovery
25
- 'nexus_describe_tool',
26
- 'nexus_search',
27
- // Health + status
28
- 'nexus_runtime_health',
29
- 'nexus_run_status',
30
- // Kernel execution primitives (bare kernel_ prefix — not nexus_)
31
- 'kernel_run_step',
32
- 'kernel_run_verify',
33
- 'kernel_run_merge',
34
- ]);
35
4
  export function buildMcpToolDefinitions() {
36
- const tools = [
5
+ return [
37
6
  // ── Memory ────────────────────────────────────────────────────────
38
7
  {
39
8
  name: 'nexus_store_memory',
@@ -1079,74 +1048,7 @@ export function buildMcpToolDefinitions() {
1079
1048
  },
1080
1049
  },
1081
1050
  },
1082
- // ── Kernel Execution (PR 2: step / verify / merge) ───────────────
1083
- {
1084
- name: 'kernel_run_step',
1085
- description: 'Run a mutation step inside an isolated git worktree and record it in the kernel ledger. Returns worktreeRef, commitSha, changedFiles. Merge requires a subsequent kernel_run_verify.',
1086
- inputSchema: {
1087
- type: 'object',
1088
- properties: {
1089
- runId: { type: 'string' },
1090
- stepId: { type: 'string' },
1091
- task: { type: 'string' },
1092
- intent: { type: 'string' },
1093
- baseRef: { type: 'string', description: 'Ref/branch/sha to branch off. Defaults to HEAD.' },
1094
- worktreeHintRef: { type: 'string', description: 'Optional branch label override.' },
1095
- mutations: {
1096
- type: 'array',
1097
- items: {
1098
- type: 'object',
1099
- properties: {
1100
- kind: { type: 'string', enum: ['write_file', 'append_file', 'delete_file', 'run_command'] },
1101
- path: { type: 'string' },
1102
- content: { type: 'string' },
1103
- command: { type: 'string' },
1104
- cwd: { type: 'string' },
1105
- },
1106
- required: ['kind'],
1107
- },
1108
- },
1109
- metadata: { type: 'object' },
1110
- },
1111
- required: ['runId', 'stepId', 'task'],
1112
- },
1113
- },
1114
- {
1115
- name: 'kernel_run_verify',
1116
- description: 'Hash the files changed by a kernel step and optionally run registered verifiers. Records the snapshot used to detect stale-verify at merge time.',
1117
- inputSchema: {
1118
- type: 'object',
1119
- properties: {
1120
- runId: { type: 'string' },
1121
- stepId: { type: 'string' },
1122
- verifierIds: { type: 'array', items: { type: 'string' } },
1123
- metadata: { type: 'object' },
1124
- },
1125
- required: ['runId', 'stepId'],
1126
- },
1127
- },
1128
- {
1129
- name: 'kernel_run_merge',
1130
- description: 'Fast-forward targetRef to a verified kernel step. Rejects when verify is missing, failed, or stale (worktree drifted after verify).',
1131
- inputSchema: {
1132
- type: 'object',
1133
- properties: {
1134
- runId: { type: 'string' },
1135
- stepId: { type: 'string' },
1136
- verifyId: { type: 'string', description: 'Explicit verify pin. If omitted, latest passed verify is used.' },
1137
- targetRef: { type: 'string' },
1138
- reason: { type: 'string' },
1139
- metadata: { type: 'object' },
1140
- },
1141
- required: ['runId', 'stepId', 'targetRef'],
1142
- },
1143
- },
1144
1051
  // ── Embedded capabilities (auto-detected from installed skills) ──
1145
1052
  ...getGstackBridge().getToolDefinitions(),
1146
1053
  ];
1147
- // Mark every non-first-class tool deprecated so agents using
1148
- // nexus_describe_tool can identify the canonical surface.
1149
- return tools.map(t => FIRST_CLASS_TOOLS.has(t.name)
1150
- ? t
1151
- : { ...t, deprecated: true, deprecatedReplacement: t.deprecatedReplacement ?? 'nexus_orchestrate' });
1152
1054
  }
@@ -14,13 +14,11 @@ import { handleRuntimeGroup } from './handlers/runtime.js';
14
14
  import { handleDiscoveryGroup } from './handlers/discovery.js';
15
15
  import { handleMiscGroup } from './handlers/misc.js';
16
16
  import { handleWorkforceGroup } from './handlers/workforce.js';
17
- import { handleKernelExecutionGroup } from './handlers/kernel-execution.js';
18
17
  import { nexusEventBus } from '../../../engines/event-bus.js';
19
18
  import { recordToolInvocation } from './tool-health.js';
20
19
  import { withAsyncGate } from './async-gate.js';
21
20
  import { getCircuitManager, checkBackpressure } from './circuit.js';
22
21
  import { createRun } from '../../../engines/orchestrator/store.js';
23
- import { FIRST_CLASS_TOOLS } from './definitions.js';
24
22
  /** Tools that may exceed the MCP client timeout (~60s). Wrap in async gate. */
25
23
  const SLOW_TOOLS = new Set([
26
24
  'nexus_orchestrate',
@@ -58,7 +56,6 @@ export async function dispatchMcpToolCall(hctx, request, args, ctx) {
58
56
  handleRuntimeGroup,
59
57
  handleDiscoveryGroup,
60
58
  handleWorkforceGroup,
61
- handleKernelExecutionGroup,
62
59
  handleMiscGroup,
63
60
  ];
64
61
  for (const group of groups) {
@@ -150,9 +147,6 @@ export async function dispatchMcpToolCall(hctx, request, args, ctx) {
150
147
  circuit.recordComplete(toolName, durationMs, true);
151
148
  recordToolInvocation(toolName, durationMs, 'ok');
152
149
  nexusEventBus.emit('tool.invocation', { toolName, durationMs, status: 'ok' });
153
- if (!FIRST_CLASS_TOOLS.has(toolName)) {
154
- nexusEventBus.emit('nexus.deprecation.used', { toolName });
155
- }
156
150
  }
157
151
  return rawResult;
158
152
  }
@@ -3,10 +3,10 @@
3
3
  * graph_query, hypertune_max.
4
4
  * Extracted from mcp.ts (Phase 3 split).
5
5
  */
6
- import { formatBullets, formatJsonDetails, getGraphEngine, getTraversalEngine, getHybridRetriever, } from '../helpers.js';
6
+ import { formatBullets, formatJsonDetails, getTokenEngine, getGraphEngine, getTraversalEngine, getHybridRetriever, } from '../helpers.js';
7
7
  import { nexusEventBus } from '../../../../engines/event-bus.js';
8
- import { formatBrandedResponse } from '../../../../adapters/shared/formatters/branded-response.js';
9
- import { formatReadingPlan, getKernelContextService } from '../../../../kernel/index.js';
8
+ import { formatBrandedResponse } from '../../../../engines/nexus-brand.js';
9
+ import { formatReadingPlan } from '../../../../engines/token-supremacy.js';
10
10
  import { ContextAssembler } from '../../../../engines/context-assembler.js';
11
11
  import { pLimit } from '../../../../engines/util/p-limit.js';
12
12
  import { promises as fsPromises, statSync } from 'fs';
@@ -289,7 +289,7 @@ export async function handleGovernanceGroup(toolName, hctx, request, args, ctx)
289
289
  return { path: resolved, sizeBytes: 0 };
290
290
  }
291
291
  })))).filter((f) => f.sizeBytes > 0);
292
- const { plan, assembly, budgetConfig } = getKernelContextService().hypertuneMax(task, files);
292
+ const { plan, assembly, budgetConfig } = getTokenEngine().hypertuneMax(task, files);
293
293
  const planText = formatReadingPlan(plan);
294
294
  const assemblyText = ContextAssembler.format(assembly, budgetConfig);
295
295
  const savings = plan.savings;
@@ -3,12 +3,12 @@
3
3
  * mindkit_check, ghost_pass, spawn_workers, session_dna, plan_execution.
4
4
  * Extracted from mcp.ts (Phase 3 split).
5
5
  */
6
- import { normalizeDetailLevel, normalizeBootstrapDepth, normalizeResponseIntent, normalizeResponseTopology, buildWorkspaceEnvelope, formatBullets, formatJsonDetails, getGuardrailEngine, truncateText, } from '../helpers.js';
6
+ import { normalizeDetailLevel, normalizeBootstrapDepth, normalizeResponseIntent, normalizeResponseTopology, buildWorkspaceEnvelope, formatBullets, formatJsonDetails, getTokenEngine, getGuardrailEngine, truncateText, } from '../helpers.js';
7
7
  import { nexusEventBus } from '../../../../engines/event-bus.js';
8
- import { formatBrandedResponse, getRandomQuote } from '../../../../adapters/shared/formatters/branded-response.js';
8
+ import { formatBrandedResponse, getRandomQuote } from '../../../../engines/nexus-brand.js';
9
9
  import { hasValidBootstrapReceipt } from '../../../../engines/bootstrap/index.js';
10
10
  import { resolveWorkspaceContext, resolveWorkspaceStateRoot } from '../../../../engines/workspace-resolver.js';
11
- import { formatReadingPlan, kernel, getKernelContextService, KernelContextService, } from '../../../../kernel/index.js';
11
+ import { formatReadingPlan } from '../../../../engines/token-supremacy.js';
12
12
  import { GhostPass, summarizeExecution } from '../../../../phantom/index.js';
13
13
  import { applyExecutionPreset, resolveExecutionPreset } from '../../../../engines/execution-presets.js';
14
14
  import { pLimit } from '../../../../engines/util/p-limit.js';
@@ -39,9 +39,7 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
39
39
  try {
40
40
  await Promise.race([
41
41
  hctx.nexusRef.awaitReady(),
42
- new Promise((_, reject) => {
43
- AbortSignal.timeout(8000).addEventListener('abort', () => reject(new Error('nexus_startup_timeout')));
44
- }),
42
+ new Promise((_, reject) => setTimeout(() => reject(new Error('nexus_startup_timeout')), 8000)),
45
43
  ]);
46
44
  }
47
45
  catch {
@@ -295,7 +293,7 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
295
293
  detailLevel === 'debug' && bootstrap.tokenOptimization?.autoApplied && bootstrap.tokenOptimization?.plan
296
294
  ? `Auto token plan\n\`\`\`txt\n${bootstrap.tokenOptimization.plan}\n\`\`\``
297
295
  : '',
298
- formatJsonDetails('Structured details', payload),
296
+ detailLevel === 'debug' ? formatJsonDetails('Structured details', payload) : '',
299
297
  hctx.formatProtocolChecklist(),
300
298
  ].filter(Boolean).join('\n\n'),
301
299
  }],
@@ -352,24 +350,13 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
352
350
  return { path: resolved, sizeBytes: 0 };
353
351
  }
354
352
  })));
355
- const upfrontPack = await kernel.run.compileContext({
356
- runId: `orchestrate-preflight-${Date.now()}`,
357
- host: 'mcp',
358
- model: 'unknown',
359
- task: prompt,
360
- files: fileRefs.map((f) => f.path),
361
- });
362
- const savings = upfrontPack.tokenPlan.savings ?? 0;
363
- if (savings > 0) {
364
- const totalInput = upfrontPack.tokenPlan.estimatedInputTokens;
365
- const pct = totalInput > 0
366
- ? Math.round(savings / (totalInput + savings) * 100)
353
+ const upfrontPlan = await getTokenEngine().plan(prompt, fileRefs);
354
+ if (upfrontPlan.savings > 0) {
355
+ const pct = upfrontPlan.totalEstimatedTokens > 0
356
+ ? Math.round(upfrontPlan.savings / (upfrontPlan.totalEstimatedTokens + upfrontPlan.savings) * 100)
367
357
  : 0;
368
- const fullReads = upfrontPack.selectedFiles.filter((f) => f.mode === 'full').length;
369
- const outlines = upfrontPack.selectedFiles.filter((f) => f.mode === 'outline').length;
370
- const skipped = upfrontPack.omittedFiles.length;
371
- upfrontTokenNote = `Token plan: ${fullReads} full reads, ${outlines} outlines, ${skipped} skipped. ~${savings.toLocaleString()} tokens saved (${pct}%). `;
372
- nexusEventBus.emit('tokens.optimized', { savings, pct, source: 'orchestrate-preflight' });
358
+ upfrontTokenNote = `Token plan: ${upfrontPlan.files.filter(a => a.action === 'full').length} full reads, ${upfrontPlan.files.filter(a => a.action === 'outline').length} outlines, ${upfrontPlan.files.filter(a => a.action === 'skip').length} skipped. ~${upfrontPlan.savings.toLocaleString()} tokens saved (${pct}%). `;
359
+ nexusEventBus.emit('tokens.optimized', { savings: upfrontPlan.savings, pct, source: 'orchestrate-preflight' });
373
360
  }
374
361
  }
375
362
  catch {
@@ -511,7 +498,6 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
511
498
  `Crew: ${execution.plannerState?.selectedCrew?.name || 'baseline path'}`,
512
499
  `Verification: ${verifiedWorkers}/${execution.workerResults.length} worker(s) verified`,
513
500
  `Tokens: saved ${Number(execution.tokenTelemetry?.savedTokens || 0).toLocaleString()} · compression ${Number(execution.tokenTelemetry?.compressionPct || 0)}%`,
514
- `Payload ref: ${workspace.stateKey} · ${detailLevel}`,
515
501
  autoTokenApplyNote || null,
516
502
  ...(detailLevel === 'debug' ? [
517
503
  `Specialists: ${execution.plannerState?.selectedSpecialists.map((specialist) => specialist.name).slice(0, 4).join(', ') || 'none selected'}`,
@@ -526,7 +512,7 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
526
512
  ] : []),
527
513
  ]),
528
514
  detailLevel === 'debug' && execution.result ? `Result\n\`\`\`\n${execution.result}\n\`\`\`` : `Result preview\n\`\`\`\n${truncateText(execution.result || summarizeExecution(execution), 900)}\n\`\`\``,
529
- formatJsonDetails('Structured details', payload),
515
+ detailLevel === 'debug' ? formatJsonDetails('Structured details', payload) : '',
530
516
  hctx.formatRemainingProtocolSteps(),
531
517
  ].filter(Boolean).join('\n\n'),
532
518
  }],
@@ -582,23 +568,11 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
582
568
  }
583
569
  })));
584
570
  // Differential: files unchanged since last session cost 0 tokens (cross-session dedup)
585
- const kernelCtx = getKernelContextService();
586
- const lastSession = await kernelCtx.loadLatestSessionSummary();
587
- const delta = lastSession ? kernelCtx.differential(lastSession, files) : null;
571
+ const engine = getTokenEngine();
572
+ const lastSession = await engine.loadLatestSessionSummary();
573
+ const delta = lastSession ? engine.differential(lastSession, files) : null;
588
574
  const planFiles = delta ? [...delta.added, ...delta.changed] : files;
589
- const pack = await kernelCtx.compileContext({
590
- runId: `token-${Date.now()}`,
591
- host: 'mcp',
592
- model: 'unknown',
593
- task,
594
- files: planFiles.map(f => f.path),
595
- });
596
- const plan = KernelContextService.extractReadingPlan(pack) ?? {
597
- task,
598
- files: [],
599
- savings: 0,
600
- totalEstimatedTokens: 0,
601
- };
575
+ const plan = await engine.plan(task, planFiles);
602
576
  // Inject unchanged files as cached-skip entries; add their token cost to savings
603
577
  const dedupTokens = delta
604
578
  ? delta.unchanged.reduce((sum, f) => sum + Math.ceil(f.sizeBytes / 4), 0)
@@ -5,6 +5,7 @@
5
5
  * These are pure functions and lazy-singleton getters shared by the
6
6
  * handler files and by MCPAdapter itself.
7
7
  */
8
+ import { TokenSupremacyEngine } from '../../../engines/token-supremacy.js';
8
9
  import { GuardrailEngine } from '../../../engines/guardrails-bridge.js';
9
10
  import { ContinuousAttentionStream, createKVBridge, OrchestratorEngine } from '../../../engines/index.js';
10
11
  import { FederationEngine } from '../../../engines/federation.js';
@@ -13,6 +14,7 @@ import { GraphTraversalEngine } from '../../../engines/graph-traversal.js';
13
14
  import { HybridRetriever } from '../../../engines/hybrid-retriever.js';
14
15
  import type { WorkspaceContext } from '../../../engines/workspace-resolver.js';
15
16
  import type { SubAgentRuntime } from '../../../phantom/index.js';
17
+ export declare function getTokenEngine(): TokenSupremacyEngine;
16
18
  export declare function getGuardrailEngine(): GuardrailEngine;
17
19
  export declare function getCasEngine(): ContinuousAttentionStream;
18
20
  export declare function getKvBridge(): ReturnType<typeof createKVBridge>;
@@ -23,8 +25,8 @@ export declare function getHybridRetriever(): HybridRetriever;
23
25
  export declare function getFallbackRuntime(workspace: WorkspaceContext): SubAgentRuntime;
24
26
  export declare function getFallbackOrchestrator(workspace: WorkspaceContext): OrchestratorEngine;
25
27
  import type { ResponseDetailLevel, ResponseIntent, ResponseTopology } from './types.js';
26
- export { formatBullets } from '../../../adapters/shared/formatters/bullets.js';
27
- export { formatJsonDetails } from '../../../adapters/shared/formatters/json-details.js';
28
+ export declare function formatBullets(lines: Array<string | null | undefined>): string;
29
+ export declare function formatJsonDetails(label: string, value: unknown): string;
28
30
  export declare function normalizeDetailLevel(value: unknown): ResponseDetailLevel;
29
31
  export declare function normalizeBootstrapDepth(value: unknown, detailLevel: ResponseDetailLevel): 'fast' | 'deep';
30
32
  export declare function normalizeResponseIntent(value: unknown): ResponseIntent;
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { statSync, readdirSync } from 'fs';
9
9
  import * as path from 'path';
10
+ import { TokenSupremacyEngine } from '../../../engines/token-supremacy.js';
10
11
  import { GuardrailEngine } from '../../../engines/guardrails-bridge.js';
11
12
  import { ContinuousAttentionStream, createKVBridge, OrchestratorEngine, } from '../../../engines/index.js';
12
13
  import { FederationEngine } from '../../../engines/federation.js';
@@ -15,6 +16,7 @@ import { GraphTraversalEngine } from '../../../engines/graph-traversal.js';
15
16
  import { HybridRetriever } from '../../../engines/hybrid-retriever.js';
16
17
  import { createSubAgentRuntime } from '../../../phantom/index.js';
17
18
  // ─── Lazy-init engine singletons ─────────────────────────────────────────────
19
+ let _tokenEngine;
18
20
  let _guardrailEngine;
19
21
  let _casEngine;
20
22
  let _kvBridge;
@@ -22,6 +24,11 @@ let _federation;
22
24
  let _graphEngine = null;
23
25
  let _traversalEngine = null;
24
26
  let _hybridRetriever = null;
27
+ export function getTokenEngine() {
28
+ if (!_tokenEngine)
29
+ _tokenEngine = new TokenSupremacyEngine();
30
+ return _tokenEngine;
31
+ }
25
32
  export function getGuardrailEngine() {
26
33
  if (!_guardrailEngine)
27
34
  _guardrailEngine = new GuardrailEngine();
@@ -82,10 +89,12 @@ export function getFallbackOrchestrator(workspace) {
82
89
  }
83
90
  return instance;
84
91
  }
85
- // PR4+5: formatters moved to src/adapters/shared/formatters. Re-exported here
86
- // so existing handler imports keep working while the adapter migration lands.
87
- export { formatBullets } from '../../../adapters/shared/formatters/bullets.js';
88
- export { formatJsonDetails } from '../../../adapters/shared/formatters/json-details.js';
92
+ export function formatBullets(lines) {
93
+ return lines.filter(Boolean).map((line) => `- ${line}`).join('\n');
94
+ }
95
+ export function formatJsonDetails(label, value) {
96
+ return `\n\n${label}\n\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``;
97
+ }
89
98
  export function normalizeDetailLevel(value) {
90
99
  const normalized = String(value ?? 'compact').toLowerCase();
91
100
  if (normalized === 'standard' || normalized === 'debug')
@@ -3,6 +3,12 @@ interface InitArchitectsOptions {
3
3
  repoRoot: string;
4
4
  requestedStorageRoot?: string;
5
5
  storageRoot?: string;
6
+ /**
7
+ * When true, bypass the opt-in `ArchitectsConfig.enabled` gate. Used by the
8
+ * lazy-init path so the first explicit Architects tool call is itself the
9
+ * opt-in that causes the DB to be created.
10
+ */
11
+ forceEnable?: boolean;
6
12
  }
7
13
  export declare function initArchitects(options: InitArchitectsOptions): ArchitectsRuntime | null;
8
14
  export {};
@@ -28,7 +28,7 @@ function parseTimestamp(value) {
28
28
  return 0;
29
29
  }
30
30
  export function initArchitects(options) {
31
- if (!ArchitectsConfig.enabled)
31
+ if (!ArchitectsConfig.enabled && !options.forceEnable)
32
32
  return null;
33
33
  let opened;
34
34
  const requestedStorageRoot = options.requestedStorageRoot ?? options.repoRoot;
@@ -0,0 +1,25 @@
1
+ import { type SweepReport } from '../install/fs-purge.js';
2
+ export interface CleanupOptions {
3
+ dryRun: boolean;
4
+ fix: boolean;
5
+ repoRoot?: string;
6
+ }
7
+ export interface CleanupBudget {
8
+ ttlMs: number;
9
+ maxCount: number;
10
+ maxBytes: number;
11
+ }
12
+ export interface CleanupReport {
13
+ worktrees: SweepReport[];
14
+ runs: SweepReport[];
15
+ orphans: {
16
+ paths: string[];
17
+ bytesReclaimed: number;
18
+ };
19
+ totalBytesReclaimed: number;
20
+ totalEntriesRemoved: number;
21
+ dryRun: boolean;
22
+ }
23
+ export declare function resolveWorktreeBudget(): CleanupBudget;
24
+ export declare function resolveRunsBudget(): CleanupBudget;
25
+ export declare function runCleanup(options: CleanupOptions): Promise<CleanupReport>;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * nexus cleanup — bound the growth of worktree + runs directories.
3
+ *
4
+ * Different from `uninstall`: this never touches persistent state. It enforces
5
+ * TTL / count / bytes budgets over transient scratch dirs so local disk usage
6
+ * can't balloon without an operator noticing.
7
+ *
8
+ * Defaults chosen to be loose enough that a busy day of work doesn't trip them:
9
+ * - TTL: 12h per worktree, 6h per run
10
+ * - maxCount: 32 worktrees, 64 runs
11
+ * - maxBytes: 2 GiB across worktrees, 1 GiB across runs
12
+ *
13
+ * Override with env: NEXUS_WORKTREE_TTL_MS, NEXUS_WORKTREE_MAX_COUNT,
14
+ * NEXUS_WORKTREE_MAX_BYTES, NEXUS_RUNS_TTL_MS, NEXUS_RUNS_MAX_COUNT,
15
+ * NEXUS_RUNS_MAX_BYTES.
16
+ */
17
+ import * as path from 'path';
18
+ import { formatBytes, sweepDirectory, sweepOrphanWorktrees, } from '../install/fs-purge.js';
19
+ import { getNexusStateDir, getWorktreeRoots, getRuntimeTmpRoots, } from '../install/state-locator.js';
20
+ function parseIntEnv(name, fallback) {
21
+ const raw = process.env[name];
22
+ const parsed = Number.parseInt(raw ?? '', 10);
23
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
24
+ }
25
+ export function resolveWorktreeBudget() {
26
+ return {
27
+ ttlMs: parseIntEnv('NEXUS_WORKTREE_TTL_MS', 12 * 60 * 60 * 1000),
28
+ maxCount: parseIntEnv('NEXUS_WORKTREE_MAX_COUNT', 32),
29
+ maxBytes: parseIntEnv('NEXUS_WORKTREE_MAX_BYTES', 2 * 1024 * 1024 * 1024),
30
+ };
31
+ }
32
+ export function resolveRunsBudget() {
33
+ return {
34
+ ttlMs: parseIntEnv('NEXUS_RUNS_TTL_MS', 6 * 60 * 60 * 1000),
35
+ maxCount: parseIntEnv('NEXUS_RUNS_MAX_COUNT', 64),
36
+ maxBytes: parseIntEnv('NEXUS_RUNS_MAX_BYTES', 1024 * 1024 * 1024),
37
+ };
38
+ }
39
+ export async function runCleanup(options) {
40
+ const dryRun = options.dryRun || !options.fix;
41
+ const repoRoot = options.repoRoot ?? process.cwd();
42
+ const worktreeBudget = resolveWorktreeBudget();
43
+ const runsBudget = resolveRunsBudget();
44
+ const report = {
45
+ worktrees: [],
46
+ runs: [],
47
+ orphans: { paths: [], bytesReclaimed: 0 },
48
+ totalBytesReclaimed: 0,
49
+ totalEntriesRemoved: 0,
50
+ dryRun,
51
+ };
52
+ const label = dryRun ? '[dry-run]' : '[cleanup]';
53
+ console.log(`\n${label} Nexus cleanup — repo: ${repoRoot}`);
54
+ // Worktree roots
55
+ for (const root of getWorktreeRoots()) {
56
+ const sweep = sweepDirectory(root, {
57
+ maxAgeMs: worktreeBudget.ttlMs,
58
+ maxCount: worktreeBudget.maxCount,
59
+ maxBytes: worktreeBudget.maxBytes,
60
+ dryRun,
61
+ keepRoot: true,
62
+ });
63
+ report.worktrees.push(sweep);
64
+ report.totalBytesReclaimed += sweep.removed.reduce((s, r) => s + r.bytes, 0);
65
+ report.totalEntriesRemoved += sweep.removed.length;
66
+ if (sweep.entriesBefore > 0 || sweep.removed.length > 0) {
67
+ console.log(` worktree: ${root}`);
68
+ console.log(` before: ${sweep.entriesBefore} entries, ${formatBytes(sweep.totalBytesBefore)}`);
69
+ console.log(` removed: ${sweep.removed.length} entries, ${formatBytes(sweep.removed.reduce((s, r) => s + r.bytes, 0))}`);
70
+ for (const r of sweep.removed) {
71
+ console.log(` - ${r.path} (${formatBytes(r.bytes)}, reason=${r.reason})`);
72
+ }
73
+ }
74
+ }
75
+ // Runs roots
76
+ for (const root of [path.join(getNexusStateDir(), 'runs'), ...getRuntimeTmpRoots()]) {
77
+ const sweep = sweepDirectory(root, {
78
+ maxAgeMs: runsBudget.ttlMs,
79
+ maxCount: runsBudget.maxCount,
80
+ maxBytes: runsBudget.maxBytes,
81
+ dryRun,
82
+ keepRoot: true,
83
+ });
84
+ report.runs.push(sweep);
85
+ report.totalBytesReclaimed += sweep.removed.reduce((s, r) => s + r.bytes, 0);
86
+ report.totalEntriesRemoved += sweep.removed.length;
87
+ if (sweep.entriesBefore > 0 || sweep.removed.length > 0) {
88
+ console.log(` runs: ${root}`);
89
+ console.log(` before: ${sweep.entriesBefore} entries, ${formatBytes(sweep.totalBytesBefore)}`);
90
+ console.log(` removed: ${sweep.removed.length} entries, ${formatBytes(sweep.removed.reduce((s, r) => s + r.bytes, 0))}`);
91
+ }
92
+ }
93
+ // Orphan worktrees (dir exists on disk but `git worktree list` forgot about it)
94
+ try {
95
+ const orphanReport = await sweepOrphanWorktrees(repoRoot, { dryRun });
96
+ report.orphans.paths = orphanReport.ops.filter((op) => op.kind !== 'missing').map((op) => op.path);
97
+ report.orphans.bytesReclaimed = orphanReport.totalBytesRemoved;
98
+ report.totalBytesReclaimed += orphanReport.totalBytesRemoved;
99
+ report.totalEntriesRemoved += report.orphans.paths.length;
100
+ if (report.orphans.paths.length > 0) {
101
+ console.log(` orphans: ${report.orphans.paths.length} worktree dir(s) not tracked by git`);
102
+ for (const p of report.orphans.paths)
103
+ console.log(` - ${p}`);
104
+ }
105
+ }
106
+ catch (err) {
107
+ console.log(` ! orphan sweep skipped: ${err instanceof Error ? err.message : String(err)}`);
108
+ }
109
+ console.log(`\n${label} reclaimed ${formatBytes(report.totalBytesReclaimed)} across ${report.totalEntriesRemoved} entries.`);
110
+ if (dryRun) {
111
+ console.log(' Preview only — re-run with --fix to apply.');
112
+ }
113
+ return report;
114
+ }
@@ -0,0 +1,19 @@
1
+ import type { StateScope } from '../install/state-locator.js';
2
+ export interface DoctorReport {
3
+ stateDir: string;
4
+ totals: Record<StateScope, number>;
5
+ entries: Array<{
6
+ path: string;
7
+ scope: StateScope;
8
+ bytes: number;
9
+ present: boolean;
10
+ }>;
11
+ flags: Array<{
12
+ scope: StateScope;
13
+ reason: string;
14
+ actual: number;
15
+ limit: number;
16
+ }>;
17
+ suggestCleanup: boolean;
18
+ }
19
+ export declare function runDoctorStorage(): DoctorReport;