nexus-prime 7.4.0 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +18 -0
  2. package/dist/adapters/hooks/context.d.ts +2 -2
  3. package/dist/adapters/hooks/index.d.ts +2 -2
  4. package/dist/agents/adapters/ide-compat.js +4 -1
  5. package/dist/agents/adapters/mcp/definitions.d.ts +3 -0
  6. package/dist/agents/adapters/mcp/definitions.js +83 -1
  7. package/dist/agents/adapters/mcp/handlers/governance.js +3 -2
  8. package/dist/agents/adapters/mcp/handlers/memory.js +8 -3
  9. package/dist/agents/adapters/mcp/handlers/orchestration.js +27 -11
  10. package/dist/agents/adapters/mcp/helpers.d.ts +0 -2
  11. package/dist/agents/adapters/mcp/helpers.js +0 -7
  12. package/dist/agents/adapters/mcp/util/token-engine.d.ts +2 -0
  13. package/dist/agents/adapters/mcp/util/token-engine.js +7 -0
  14. package/dist/agents/adapters/mcp.js +13 -12
  15. package/dist/cli/hook.d.ts +9 -1
  16. package/dist/cli/hook.js +11 -2
  17. package/dist/cli/install-wizard.js +4 -1
  18. package/dist/cli.js +100 -1
  19. package/dist/daemon/server.js +7 -8
  20. package/dist/dashboard/app/index.html +20 -1
  21. package/dist/dashboard/app/main.js +14 -0
  22. package/dist/dashboard/app/state.js +7 -0
  23. package/dist/dashboard/app/styles/tokens.css +5 -9
  24. package/dist/dashboard/app/views/board.js +186 -0
  25. package/dist/dashboard/server.js +6 -1
  26. package/dist/dashboard/stream/sse-broker.js +21 -15
  27. package/dist/engines/client-bootstrap.js +27 -3
  28. package/dist/engines/context-assembler.d.ts +1 -1
  29. package/dist/engines/context-assembler.js +12 -7
  30. package/dist/engines/event-bus.d.ts +29 -1
  31. package/dist/engines/generated-specialist-loader.js +25 -3
  32. package/dist/engines/machine-efficiency/types.js +5 -1
  33. package/dist/engines/memory.d.ts +6 -0
  34. package/dist/engines/memory.js +32 -16
  35. package/dist/engines/orchestrator.js +54 -25
  36. package/dist/engines/token-supremacy.d.ts +2 -2
  37. package/dist/engines/token-supremacy.js +4 -4
  38. package/dist/engines/workspace-resolver.js +6 -10
  39. package/dist/index.js +62 -35
  40. package/dist/kernel/context/service.d.ts +2 -2
  41. package/dist/kernel/index.d.ts +2 -2
  42. package/dist/phantom/runtime.d.ts +1 -1
  43. package/dist/phantom/runtime.js +81 -5
  44. package/package.json +4 -3
  45. package/dist/agents/adapters/mcp/util/auto-memory.d.ts +0 -15
  46. package/dist/agents/adapters/mcp/util/auto-memory.js +0 -23
package/README.md CHANGED
@@ -49,8 +49,10 @@
49
49
  - [The problem](#the-problem)
50
50
  - [What changes when you install](#what-changes-when-you-install)
51
51
  - [Quick start](#quick-start)
52
+ - [Docs and comparison](#docs-and-comparison)
52
53
  - [Features](#features)
53
54
  - [See it in action](#see-it-in-action)
55
+ - [Competitive position](#competitive-position)
54
56
  - [Works with](#works-with)
55
57
  - [What users see](#what-users-see)
56
58
  - [Who it's for](#who-its-for)
@@ -62,6 +64,16 @@
62
64
 
63
65
  ---
64
66
 
67
+ ## Docs and comparison
68
+
69
+ - Website: https://nexus-prime.cfd/
70
+ - Comparison: https://nexus-prime.cfd/comparison.html
71
+ - Knowledge base: https://nexus-prime.cfd/knowledge-base.html
72
+
73
+ Agents should start with `nexus_session_bootstrap`, then route the raw request through `nexus_orchestrate`. Auto-bootstrap runs on first non-bootstrap call for compatible clients, but the explicit bootstrap-first path remains the safest integration contract.
74
+
75
+ ---
76
+
65
77
  ## The problem
66
78
 
67
79
  Every coding agent you use starts every session cold.
@@ -269,6 +281,12 @@ No benchmarks from a press release — this is what the dashboard shows you, on
269
281
 
270
282
  ---
271
283
 
284
+ ## Competitive position
285
+
286
+ Nexus Prime is a local-first control plane for coding agents: persistent memory, workspace-scoped runtime state, dashboard observability, and MCP-native orchestration in one install. The public comparison page tracks where it differs from other agent frameworks and developer orchestrators: https://nexus-prime.cfd/comparison.html
287
+
288
+ ---
289
+
272
290
  ## Who it's for
273
291
 
274
292
  Nexus Prime is for people who take AI-assisted coding seriously:
@@ -12,11 +12,11 @@ export declare const ContextHooks: {
12
12
  readonly hypertuneMax: (task: string, files: FileRef[], graphConnectivity?: (chunk: {
13
13
  source: string;
14
14
  label: string;
15
- }) => number) => {
15
+ }) => number) => Promise<{
16
16
  plan: ReadingPlan;
17
17
  assembly: AssemblyResult;
18
18
  budgetConfig: BudgetConfig;
19
- };
19
+ }>;
20
20
  readonly differential: (prev: SessionSummary, curr: FileRef[]) => ContextDelta;
21
21
  readonly loadLatestSessionSummary: () => Promise<SessionSummary | null>;
22
22
  readonly allocateBudget: (workers: Array<{
@@ -37,11 +37,11 @@ export declare const Hooks: {
37
37
  readonly hypertuneMax: (task: string, files: FileRef[], graphConnectivity?: (chunk: {
38
38
  source: string;
39
39
  label: string;
40
- }) => number) => {
40
+ }) => number) => Promise<{
41
41
  plan: import("../../engines/token-supremacy.js").ReadingPlan;
42
42
  assembly: import("../../engines/context-assembler.js").AssemblyResult;
43
43
  budgetConfig: import("../../engines/context-assembler.js").BudgetConfig;
44
- };
44
+ }>;
45
45
  readonly differential: (prev: import("../../engines/token-supremacy.js").SessionSummary, curr: FileRef[]) => import("../../engines/token-supremacy.js").ContextDelta;
46
46
  readonly loadLatestSessionSummary: () => Promise<import("../../engines/token-supremacy.js").SessionSummary | null>;
47
47
  readonly allocateBudget: (workers: Array<{
@@ -13,7 +13,10 @@ function nexusMcpServerEntry(workspaceRoot) {
13
13
  return {
14
14
  command: 'node',
15
15
  args: [join(workspaceRoot, 'node_modules', 'nexus-prime', 'dist', 'cli.js'), 'mcp'],
16
- env: {},
16
+ env: {
17
+ NEXUS_MCP_TOOL_PROFILE: 'autonomous',
18
+ NEXUS_WORKSPACE_ROOT: workspaceRoot,
19
+ },
17
20
  };
18
21
  }
19
22
  function hasAnyEnvPrefix(prefix) {
@@ -2,5 +2,8 @@ export type McpToolDefinition = {
2
2
  name: string;
3
3
  description: string;
4
4
  inputSchema: Record<string, unknown>;
5
+ deprecated?: true;
6
+ deprecatedReplacement?: string;
5
7
  };
8
+ export declare const FIRST_CLASS_TOOLS: Set<string>;
6
9
  export declare function buildMcpToolDefinitions(): McpToolDefinition[];
@@ -1,8 +1,26 @@
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
+ export const FIRST_CLASS_TOOLS = new Set([
5
+ 'nexus_session_bootstrap',
6
+ 'nexus_orchestrate',
7
+ 'nexus_session_dna',
8
+ 'nexus_store_memory',
9
+ 'nexus_recall_memory',
10
+ 'nexus_memory_stats',
11
+ 'nexus_optimize_tokens',
12
+ 'nexus_mindkit_check',
13
+ 'nexus_ghost_pass',
14
+ 'nexus_describe_tool',
15
+ 'nexus_search',
16
+ 'nexus_runtime_health',
17
+ 'nexus_run_status',
18
+ 'kernel_run_step',
19
+ 'kernel_run_verify',
20
+ 'kernel_run_merge',
21
+ ]);
4
22
  export function buildMcpToolDefinitions() {
5
- return [
23
+ const tools = [
6
24
  // ── Memory ────────────────────────────────────────────────────────
7
25
  {
8
26
  name: 'nexus_store_memory',
@@ -1048,7 +1066,71 @@ export function buildMcpToolDefinitions() {
1048
1066
  },
1049
1067
  },
1050
1068
  },
1069
+ {
1070
+ name: 'kernel_run_step',
1071
+ 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.',
1072
+ inputSchema: {
1073
+ type: 'object',
1074
+ properties: {
1075
+ runId: { type: 'string' },
1076
+ stepId: { type: 'string' },
1077
+ task: { type: 'string' },
1078
+ intent: { type: 'string' },
1079
+ baseRef: { type: 'string', description: 'Ref/branch/sha to branch off. Defaults to HEAD.' },
1080
+ worktreeHintRef: { type: 'string', description: 'Optional branch label override.' },
1081
+ mutations: {
1082
+ type: 'array',
1083
+ items: {
1084
+ type: 'object',
1085
+ properties: {
1086
+ kind: { type: 'string', enum: ['write_file', 'append_file', 'delete_file', 'run_command'] },
1087
+ path: { type: 'string' },
1088
+ content: { type: 'string' },
1089
+ command: { type: 'string' },
1090
+ cwd: { type: 'string' },
1091
+ },
1092
+ required: ['kind'],
1093
+ },
1094
+ },
1095
+ metadata: { type: 'object' },
1096
+ },
1097
+ required: ['runId', 'stepId', 'task'],
1098
+ },
1099
+ },
1100
+ {
1101
+ name: 'kernel_run_verify',
1102
+ 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.',
1103
+ inputSchema: {
1104
+ type: 'object',
1105
+ properties: {
1106
+ runId: { type: 'string' },
1107
+ stepId: { type: 'string' },
1108
+ verifierIds: { type: 'array', items: { type: 'string' } },
1109
+ metadata: { type: 'object' },
1110
+ },
1111
+ required: ['runId', 'stepId'],
1112
+ },
1113
+ },
1114
+ {
1115
+ name: 'kernel_run_merge',
1116
+ description: 'Fast-forward targetRef to a verified kernel step. Rejects when verify is missing, failed, or stale.',
1117
+ inputSchema: {
1118
+ type: 'object',
1119
+ properties: {
1120
+ runId: { type: 'string' },
1121
+ stepId: { type: 'string' },
1122
+ verifyId: { type: 'string', description: 'Explicit verify pin. If omitted, latest passed verify is used.' },
1123
+ targetRef: { type: 'string' },
1124
+ reason: { type: 'string' },
1125
+ metadata: { type: 'object' },
1126
+ },
1127
+ required: ['runId', 'stepId', 'targetRef'],
1128
+ },
1129
+ },
1051
1130
  // ── Embedded capabilities (auto-detected from installed skills) ──
1052
1131
  ...getGstackBridge().getToolDefinitions(),
1053
1132
  ];
1133
+ return tools.map((tool) => FIRST_CLASS_TOOLS.has(tool.name)
1134
+ ? tool
1135
+ : { ...tool, deprecated: true, deprecatedReplacement: tool.deprecatedReplacement ?? 'nexus_orchestrate' });
1054
1136
  }
@@ -3,7 +3,8 @@
3
3
  * graph_query, hypertune_max.
4
4
  * Extracted from mcp.ts (Phase 3 split).
5
5
  */
6
- import { formatBullets, formatJsonDetails, getTokenEngine, getGraphEngine, getTraversalEngine, getHybridRetriever, } from '../helpers.js';
6
+ import { formatBullets, formatJsonDetails, getGraphEngine, getTraversalEngine, getHybridRetriever, } from '../helpers.js';
7
+ import { getTokenEngine } from '../util/token-engine.js';
7
8
  import { nexusEventBus } from '../../../../engines/event-bus.js';
8
9
  import { formatBrandedResponse } from '../../../../engines/nexus-brand.js';
9
10
  import { formatReadingPlan } from '../../../../engines/token-supremacy.js';
@@ -289,7 +290,7 @@ export async function handleGovernanceGroup(toolName, hctx, request, args, ctx)
289
290
  return { path: resolved, sizeBytes: 0 };
290
291
  }
291
292
  })))).filter((f) => f.sizeBytes > 0);
292
- const { plan, assembly, budgetConfig } = getTokenEngine().hypertuneMax(task, files);
293
+ const { plan, assembly, budgetConfig } = await getTokenEngine().hypertuneMax(task, files);
293
294
  const planText = formatReadingPlan(plan);
294
295
  const assemblyText = ContextAssembler.format(assembly, budgetConfig);
295
296
  const savings = plan.savings;
@@ -7,7 +7,6 @@
7
7
  import { formatBullets, formatJsonDetails } from '../helpers.js';
8
8
  import { nexusEventBus } from '../../../../engines/event-bus.js';
9
9
  import { getOperativeJournal } from '../../../../engines/operative-journal.js';
10
- import { storeToolSummary } from '../util/auto-memory.js';
11
10
  import { requireRuntime } from '../util/require-runtime.js';
12
11
  import { nxlResult } from '../../../../nxl/index.js';
13
12
  import { consolidateMemory } from '../../../../engines/memory-consolidator.js';
@@ -368,9 +367,15 @@ export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
368
367
  states,
369
368
  auditLimit: limit,
370
369
  });
371
- // Record hygiene sweep so dashboard memory view shows recent housekeeping.
370
+ // Surface hygiene-sweep on the SSE feed; do not persist as a memory.
372
371
  try {
373
- storeToolSummary(hctx, 'nexus_memory_hygiene', `Hygiene sweep (${mode}): scanned ${result.audit.scanned}, purge-eligible ${result.health.purgeEligible}, quarantined ${result.health.quarantineBacklog}.`);
372
+ nexusEventBus.emit('memory.snapshot', {
373
+ kind: 'hygiene-sweep',
374
+ mode,
375
+ scanned: result.audit.scanned,
376
+ purgeEligible: result.health.purgeEligible,
377
+ quarantineBacklog: result.health.quarantineBacklog,
378
+ });
374
379
  }
375
380
  catch { /* best-effort */ }
376
381
  return {
@@ -3,7 +3,8 @@
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, getTokenEngine, getGuardrailEngine, truncateText, } from '../helpers.js';
6
+ import { normalizeDetailLevel, normalizeBootstrapDepth, normalizeResponseIntent, normalizeResponseTopology, buildWorkspaceEnvelope, formatBullets, formatJsonDetails, getGuardrailEngine, truncateText, } from '../helpers.js';
7
+ import { getTokenEngine } from '../util/token-engine.js';
7
8
  import { nexusEventBus } from '../../../../engines/event-bus.js';
8
9
  import { formatBrandedResponse, getRandomQuote } from '../../../../engines/nexus-brand.js';
9
10
  import { hasValidBootstrapReceipt } from '../../../../engines/bootstrap/index.js';
@@ -16,7 +17,6 @@ import { promises as fsPromises } from 'fs';
16
17
  import { getSharedLicenseManager } from '../../../../licensing/index.js';
17
18
  import { SessionDNAManager } from '../../../../engines/session-dna.js';
18
19
  import { getSharedTelemetry } from '../../../../engines/telemetry-remote.js';
19
- import { storeToolSummary } from '../util/auto-memory.js';
20
20
  import { requireRuntime } from '../util/require-runtime.js';
21
21
  import { ensureCrGraphBuilt } from '../../../../engines/code-review-graph-client.js';
22
22
  import { recordFirstBootstrap } from '../../../../engines/telemetry.js';
@@ -136,10 +136,16 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
136
136
  ctx.meta.repoRoot = workspace.repoRoot;
137
137
  ctx.meta.repoName = workspace.repoName;
138
138
  }
139
- // Auto-generate a project-scoped memory on bootstrap so dashboard always has something to show.
140
- // Priority 0.4 (via storeToolSummary) ensures it passes the SSE noise filter (threshold: 0.3).
139
+ // Surface a session-bootstrap signal on the SSE feed without polluting persistent
140
+ // memory. Audit-log content like "Session bootstrap: ..." was previously stored as
141
+ // a real memory (priority 0.4) which dominated recall results and forced low-signal
142
+ // memories into hippocampus.
141
143
  try {
142
- storeToolSummary(hctx, 'nexus_session_bootstrap', `Session bootstrap: ${bootstrapGoal || 'interactive session'}. Memory: prefrontal=${bootstrap.memoryStats?.prefrontal ?? 0}, hippocampus=${bootstrap.memoryStats?.hippocampus ?? 0}, cortex=${bootstrap.memoryStats?.cortex ?? 0}.`);
144
+ nexusEventBus.emit('memory.snapshot', {
145
+ kind: 'session-bootstrap',
146
+ goal: bootstrapGoal || 'interactive session',
147
+ memoryStats: bootstrap.memoryStats,
148
+ });
143
149
  }
144
150
  catch (err) {
145
151
  nexusEventBus.emit('mcp.handler.best-effort-failed', { toolName, stage: 'persist-session-summary', error: err instanceof Error ? err.message : String(err) });
@@ -411,7 +417,10 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
411
417
  result.modifiedFiles.forEach((file) => hctx.sessionDNA.recordFileModified(file));
412
418
  });
413
419
  const runtimeUsage = hctx.getRuntime().getUsageSnapshot();
414
- hctx.sessionDNA.recordDecision('Orchestrated autonomous run completed', execution.result || summarizeExecution(execution), execution.state === 'merged' ? 0.95 : execution.state === 'rolled_back' ? 0.55 : 0.3);
420
+ hctx.sessionDNA.recordDecision('Orchestrated autonomous run completed', execution.result || summarizeExecution(execution), execution.state === 'merged' ? 0.95
421
+ : execution.state === 'inspected' ? 0.85
422
+ : execution.state === 'rolled_back' ? 0.55
423
+ : 0.3);
415
424
  const timings = {
416
425
  totalMs: Date.now() - callStartedAt,
417
426
  };
@@ -738,10 +747,14 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
738
747
  ? String(request.params.arguments.executionPreset)
739
748
  : undefined;
740
749
  const preset = resolveExecutionPreset(executionPreset);
741
- // Record worker plan intent before execution so the dashboard memory view
742
- // shows something even if the run is still in progress.
750
+ // Surface worker-plan intent on the SSE feed without persisting it as a memory.
743
751
  try {
744
- storeToolSummary(hctx, 'nexus_spawn_workers', `Worker plan: "${goal.slice(0, 100)}" — ${workersCount} workers, ${rawFiles.length} files.`);
752
+ nexusEventBus.emit('memory.snapshot', {
753
+ kind: 'worker-plan',
754
+ goal: goal.slice(0, 100),
755
+ workers: workersCount,
756
+ files: rawFiles.length,
757
+ });
745
758
  }
746
759
  catch (err) {
747
760
  nexusEventBus.emit('mcp.handler.best-effort-failed', { toolName, stage: 'worker-plan-summary', error: err instanceof Error ? err.message : String(err) });
@@ -775,7 +788,10 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
775
788
  execution.workerResults.forEach(result => {
776
789
  result.modifiedFiles.forEach(file => hctx.sessionDNA.recordFileModified(file));
777
790
  });
778
- hctx.sessionDNA.recordDecision('Runtime swarm execution completed', execution.result || summarizeExecution(execution), execution.state === 'merged' ? 0.94 : execution.state === 'rolled_back' ? 0.45 : 0.3);
791
+ hctx.sessionDNA.recordDecision('Runtime swarm execution completed', execution.result || summarizeExecution(execution), execution.state === 'merged' ? 0.94
792
+ : execution.state === 'inspected' ? 0.85
793
+ : execution.state === 'rolled_back' ? 0.45
794
+ : 0.3);
779
795
  hctx.nexusRef.storeMemory(`Runtime swarm: state=${execution.state}, workers=${execution.workerResults.length}, verified=${verifiedWorkers}, decision=${execution.finalDecision?.action ?? 'none'}`, execution.state === 'merged' ? 0.92 : 0.72, ['#phantom', '#decision', execution.state]);
780
796
  if (execution.finalDecision) {
781
797
  nexusEventBus.emit('phantom.merge', {
@@ -789,7 +805,7 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
789
805
  `Workers: ${execution.workerResults.length.toString().padEnd(5, ' ')} Verified: ${verifiedWorkers.toString().padEnd(10, ' ')} Files: ${String(modifiedFiles).padEnd(12, ' ')}`,
790
806
  `${`Preset: ${preset?.name ?? 'manual'}`.padEnd(61, ' ')}`,
791
807
  `Decision: ${(execution.finalDecision?.action ?? 'none').padEnd(52, ' ')}`
792
- ], execution.state === 'merged' ? '32' : execution.state === 'rolled_back' ? '33' : '31');
808
+ ], execution.state === 'merged' || execution.state === 'inspected' ? '32' : execution.state === 'rolled_back' ? '33' : '31');
793
809
  return {
794
810
  content: [{
795
811
  type: 'text',
@@ -5,7 +5,6 @@
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';
9
8
  import { GuardrailEngine } from '../../../engines/guardrails-bridge.js';
10
9
  import { ContinuousAttentionStream, createKVBridge, OrchestratorEngine } from '../../../engines/index.js';
11
10
  import { FederationEngine } from '../../../engines/federation.js';
@@ -14,7 +13,6 @@ import { GraphTraversalEngine } from '../../../engines/graph-traversal.js';
14
13
  import { HybridRetriever } from '../../../engines/hybrid-retriever.js';
15
14
  import type { WorkspaceContext } from '../../../engines/workspace-resolver.js';
16
15
  import type { SubAgentRuntime } from '../../../phantom/index.js';
17
- export declare function getTokenEngine(): TokenSupremacyEngine;
18
16
  export declare function getGuardrailEngine(): GuardrailEngine;
19
17
  export declare function getCasEngine(): ContinuousAttentionStream;
20
18
  export declare function getKvBridge(): ReturnType<typeof createKVBridge>;
@@ -7,7 +7,6 @@
7
7
  */
8
8
  import { statSync, readdirSync } from 'fs';
9
9
  import * as path from 'path';
10
- import { TokenSupremacyEngine } from '../../../engines/token-supremacy.js';
11
10
  import { GuardrailEngine } from '../../../engines/guardrails-bridge.js';
12
11
  import { ContinuousAttentionStream, createKVBridge, OrchestratorEngine, } from '../../../engines/index.js';
13
12
  import { FederationEngine } from '../../../engines/federation.js';
@@ -16,7 +15,6 @@ import { GraphTraversalEngine } from '../../../engines/graph-traversal.js';
16
15
  import { HybridRetriever } from '../../../engines/hybrid-retriever.js';
17
16
  import { createSubAgentRuntime } from '../../../phantom/index.js';
18
17
  // ─── Lazy-init engine singletons ─────────────────────────────────────────────
19
- let _tokenEngine;
20
18
  let _guardrailEngine;
21
19
  let _casEngine;
22
20
  let _kvBridge;
@@ -24,11 +22,6 @@ let _federation;
24
22
  let _graphEngine = null;
25
23
  let _traversalEngine = null;
26
24
  let _hybridRetriever = null;
27
- export function getTokenEngine() {
28
- if (!_tokenEngine)
29
- _tokenEngine = new TokenSupremacyEngine();
30
- return _tokenEngine;
31
- }
32
25
  export function getGuardrailEngine() {
33
26
  if (!_guardrailEngine)
34
27
  _guardrailEngine = new GuardrailEngine();
@@ -0,0 +1,2 @@
1
+ import { TokenSupremacyEngine } from '../../../../engines/token-supremacy.js';
2
+ export declare function getTokenEngine(): TokenSupremacyEngine;
@@ -0,0 +1,7 @@
1
+ import { TokenSupremacyEngine } from '../../../../engines/token-supremacy.js';
2
+ let tokenEngine;
3
+ export function getTokenEngine() {
4
+ if (!tokenEngine)
5
+ tokenEngine = new TokenSupremacyEngine();
6
+ return tokenEngine;
7
+ }
@@ -830,21 +830,22 @@ export class MCPAdapter {
830
830
  : { error: envelope.error.message }),
831
831
  });
832
832
  this.telemetry.observeSuccessfulToolCall(toolName, args);
833
- // Server-side auto-memory: write a low-priority tagged memory after every
834
- // meaningful tool call so MCP clients without hooks (Codex, Cursor) still
835
- // accumulate context automatically. Fire-and-forget never delays response.
836
- // The isNoiseEvent filter in sse-broker.ts drops auto-memories (priority < 0.3)
837
- // from the live SSE feed, so the dashboard event stream stays clean.
833
+ // Surface meaningful tool calls on the SSE feed without persisting them as memories.
834
+ // Previously a low-priority memory was auto-stored on every non-read-only tool call,
835
+ // which polluted recall with audit-log entries like "nexus_store_memory: priority=0.85,
836
+ // tags=...". Recall now stays focused on user-stored knowledge.
838
837
  if (this.nexusRef && !isReadOnlyMcpTool(toolName)) {
839
838
  const repoRoot = this.nexusRef.getWorkspaceContext?.()?.repoRoot ?? '';
840
839
  const repoName = repoRoot ? path.basename(repoRoot) : '';
841
- const summary = buildAutoMemorySummary(toolName, args);
842
- setImmediate(() => {
843
- try {
844
- this.nexusRef.storeMemory(summary, 0.2, ['#auto', `tool:${toolName}`, ...(repoName ? [`repo:${repoName}`] : [])], undefined, undefined, { verbatim: false });
845
- }
846
- catch { /* best-effort; memory engine may not be ready */ }
847
- });
840
+ try {
841
+ nexusEventBus.emit('memory.snapshot', {
842
+ kind: 'tool-invocation',
843
+ toolName,
844
+ repo: repoName,
845
+ summary: buildAutoMemorySummary(toolName, args),
846
+ });
847
+ }
848
+ catch { /* best-effort */ }
848
849
  }
849
850
  const decorated = this.decorateLifecycleResponse(toolName, result);
850
851
  return this.injectMemoryContext(toolName, args, decorated);
@@ -16,7 +16,15 @@
16
16
  */
17
17
  /** UserPromptSubmit — call nexus_session_bootstrap so the model never has to. */
18
18
  export declare function runHookBootstrap(): Promise<void>;
19
- /** PostToolUse (Edit|Write|MultiEdit|Bash) — auto-write low-priority memory. */
19
+ /**
20
+ * PostToolUse (Edit|Write|MultiEdit|Bash) — opt-in audit memory.
21
+ *
22
+ * Disabled by default because every Edit/Write/Bash was being stored as a
23
+ * low-priority memory, which polluted recall results with audit-log entries
24
+ * like "Edit: file_path=src/foo.ts". Real findings should be stored via
25
+ * nexus_store_memory by the agent itself. To opt back in, set
26
+ * NEXUS_HOOK_AUTO_MEMORY=1.
27
+ */
20
28
  export declare function runHookMemory(): Promise<void>;
21
29
  /**
22
30
  * PreToolUse (Edit|Write|MultiEdit) — governance check.
package/dist/cli/hook.js CHANGED
@@ -149,8 +149,18 @@ export async function runHookBootstrap() {
149
149
  goal: goal.slice(0, 500),
150
150
  }, 10_000);
151
151
  }
152
- /** PostToolUse (Edit|Write|MultiEdit|Bash) — auto-write low-priority memory. */
152
+ /**
153
+ * PostToolUse (Edit|Write|MultiEdit|Bash) — opt-in audit memory.
154
+ *
155
+ * Disabled by default because every Edit/Write/Bash was being stored as a
156
+ * low-priority memory, which polluted recall results with audit-log entries
157
+ * like "Edit: file_path=src/foo.ts". Real findings should be stored via
158
+ * nexus_store_memory by the agent itself. To opt back in, set
159
+ * NEXUS_HOOK_AUTO_MEMORY=1.
160
+ */
153
161
  export async function runHookMemory() {
162
+ if (process.env.NEXUS_HOOK_AUTO_MEMORY !== '1')
163
+ return;
154
164
  const data = await readStdinJson();
155
165
  const cwd = String(data.cwd ?? process.cwd());
156
166
  const toolName = String(data.tool_name ?? '');
@@ -163,7 +173,6 @@ export async function runHookMemory() {
163
173
  if (!lock)
164
174
  return;
165
175
  const input = (data.tool_input ?? {});
166
- // Summarize input without large content blobs
167
176
  const inputSummary = Object.entries(input)
168
177
  .filter(([k]) => !['content', 'new_string', 'old_string', 'result', 'command_result'].includes(k))
169
178
  .map(([k, v]) => `${k}=${String(v).slice(0, 80)}`)
@@ -139,7 +139,10 @@ function _nexusMcpEntry(workspaceRoot) {
139
139
  return {
140
140
  command: 'node',
141
141
  args: [join(workspaceRoot, 'node_modules', 'nexus-prime', 'dist', 'cli.js'), 'mcp'],
142
- env: {},
142
+ env: {
143
+ NEXUS_MCP_TOOL_PROFILE: 'autonomous',
144
+ NEXUS_WORKSPACE_ROOT: workspaceRoot,
145
+ },
143
146
  };
144
147
  }
145
148
  /**
package/dist/cli.js CHANGED
@@ -780,6 +780,14 @@ program
780
780
  .addCommand(new Command('__serve')
781
781
  .description('Internal: run the daemon server process (do not call directly)')
782
782
  .action(async () => {
783
+ // Opt the daemon into the deferred-bootstrap fast-start path so the
784
+ // dashboard port listens before the (3+ second) workspace bootstrap
785
+ // file I/O completes. Tests that construct NexusPrime directly do not
786
+ // set this flag, so their synchronous workspace-file assertions still
787
+ // see a fully-initialized state by the time start() resolves.
788
+ if (process.env.NEXUS_DAEMON_FAST_START === undefined) {
789
+ process.env.NEXUS_DAEMON_FAST_START = '1';
790
+ }
783
791
  const workspaceContext = resolveWorkspaceContext({ workspaceRoot: getWorkspaceRoot() });
784
792
  const daemon = new NexusDaemonServer(workspaceContext);
785
793
  const { started, record } = await daemon.start();
@@ -1897,6 +1905,75 @@ program
1897
1905
  timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
1898
1906
  });
1899
1907
  });
1908
+ program
1909
+ .command('fix-claude-hooks')
1910
+ .description('Strip stale Nexus hook entries from ~/.claude/settings.json (use after downgrading from a newer version)')
1911
+ .option('--dry-run', 'Show what would change without writing')
1912
+ .option('--workspace', 'Also clean ./.claude/settings.json in the current workspace')
1913
+ .action(async (opts) => {
1914
+ const fs = await import('fs');
1915
+ const path = await import('path');
1916
+ const os = await import('os');
1917
+ const { NEXUS_HOOK_COMMAND_MARKER } = await import('./install/claude-code-hooks.js');
1918
+ const targets = [path.join(os.homedir(), '.claude', 'settings.json')];
1919
+ if (opts.workspace)
1920
+ targets.push(path.join(process.cwd(), '.claude', 'settings.json'));
1921
+ let totalRemoved = 0;
1922
+ for (const target of targets) {
1923
+ if (!fs.existsSync(target)) {
1924
+ console.log(` skip: ${target} (not found)`);
1925
+ continue;
1926
+ }
1927
+ let parsed;
1928
+ try {
1929
+ parsed = JSON.parse(fs.readFileSync(target, 'utf8'));
1930
+ }
1931
+ catch (err) {
1932
+ console.error(` error: ${target} (invalid JSON: ${err instanceof Error ? err.message : String(err)})`);
1933
+ continue;
1934
+ }
1935
+ const hooks = parsed?.hooks;
1936
+ if (!hooks || typeof hooks !== 'object') {
1937
+ console.log(` ok: ${target} (no hooks block)`);
1938
+ continue;
1939
+ }
1940
+ let removedHere = 0;
1941
+ for (const event of Object.keys(hooks)) {
1942
+ if (!Array.isArray(hooks[event]))
1943
+ continue;
1944
+ const before = hooks[event];
1945
+ const after = before.filter((entry) => {
1946
+ const entryHooks = entry?.hooks;
1947
+ if (!Array.isArray(entryHooks))
1948
+ return true;
1949
+ const hasNexus = entryHooks.some((h) => typeof h?.command === 'string' && h.command.trimStart().startsWith(NEXUS_HOOK_COMMAND_MARKER));
1950
+ if (hasNexus)
1951
+ removedHere += 1;
1952
+ return !hasNexus;
1953
+ });
1954
+ if (after.length === 0) {
1955
+ delete hooks[event];
1956
+ }
1957
+ else {
1958
+ hooks[event] = after;
1959
+ }
1960
+ }
1961
+ if (Object.keys(hooks).length === 0)
1962
+ delete parsed.hooks;
1963
+ totalRemoved += removedHere;
1964
+ if (removedHere === 0) {
1965
+ console.log(` ok: ${target} (no Nexus entries)`);
1966
+ continue;
1967
+ }
1968
+ if (opts.dryRun) {
1969
+ console.log(` dry-run: ${target} would remove ${removedHere} Nexus hook entry/entries`);
1970
+ continue;
1971
+ }
1972
+ fs.writeFileSync(target, JSON.stringify(parsed, null, 2) + '\n', 'utf8');
1973
+ console.log(` fixed: ${target} (removed ${removedHere} entry/entries)`);
1974
+ }
1975
+ console.log(opts.dryRun ? `\nDry-run total: ${totalRemoved} entry/entries.` : `\nDone. Removed ${totalRemoved} stale Nexus hook entry/entries.`);
1976
+ });
1900
1977
  program
1901
1978
  .command('watch')
1902
1979
  .description('Tail live SSE events from the running daemon in a colored terminal feed')
@@ -1915,4 +1992,26 @@ program
1915
1992
  if (process.argv.length === 2) {
1916
1993
  process.argv.push('setup', 'auto');
1917
1994
  }
1918
- program.parse();
1995
+ // Graceful fallback for unknown `nexus-prime hook <subcommand>` invocations.
1996
+ // When users downgrade from a version that wrote hook entries to
1997
+ // ~/.claude/settings.json, the older binary may not have the referenced
1998
+ // subcommand. Exiting non-zero would fail every Claude Code session start.
1999
+ // Drain piped stdin (Claude pipes JSON into hooks) and exit 0 silently.
2000
+ const KNOWN_HOOK_SUBCOMMANDS = new Set([
2001
+ 'bootstrap', 'memory', 'mindkit', 'ghost-pass', 'session-dna',
2002
+ 'generate', 'deploy', 'undeploy', 'list', 'help', '--help', '-h',
2003
+ ]);
2004
+ if (process.argv[2] === 'hook' && process.argv[3] && !KNOWN_HOOK_SUBCOMMANDS.has(process.argv[3])) {
2005
+ if (!process.stdin.isTTY) {
2006
+ process.stdin.resume();
2007
+ process.stdin.on('data', () => { });
2008
+ process.stdin.on('end', () => process.exit(0));
2009
+ setTimeout(() => process.exit(0), 200).unref();
2010
+ }
2011
+ else {
2012
+ process.exit(0);
2013
+ }
2014
+ }
2015
+ else {
2016
+ program.parse();
2017
+ }
@@ -185,14 +185,13 @@ export class NexusDaemonServer {
185
185
  },
186
186
  });
187
187
  await this.nexus.start();
188
- // Pre-warm the n-gram repo-search index so the first nexus_orchestrate
189
- // / nexus_optimize_tokens call doesn't pay the cold-build cost (Codex
190
- // hard-codes a 120s tool-call timeout). setImmediate keeps it off the
191
- // accept loop. Failures are deliberately swallowed — the on-demand path
192
- // will rebuild the index if pre-warm fails.
193
- setImmediate(() => {
194
- void this.nexus?.getOrchestrator().warmRepoSearchIndex().catch(() => { });
195
- });
188
+ // Optional repo-search prewarm. It can be CPU-heavy in large workspaces, so
189
+ // keep daemon startup and dashboard responsiveness as the default behavior.
190
+ if (process.env.NEXUS_PREWARM_REPO_SEARCH === '1' && this.workspace.gitRoot) {
191
+ setImmediate(() => {
192
+ void this.nexus?.getOrchestrator().warmRepoSearchIndex().catch(() => { });
193
+ });
194
+ }
196
195
  // Warm the invoker availability cache so /api/runtimes returns fast and
197
196
  // the dashboard runtime badges render immediately on first load.
198
197
  setImmediate(() => { invokerRegistry.warmCache(); });