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.
- package/README.md +18 -0
- package/dist/adapters/hooks/context.d.ts +2 -2
- package/dist/adapters/hooks/index.d.ts +2 -2
- package/dist/agents/adapters/ide-compat.js +4 -1
- package/dist/agents/adapters/mcp/definitions.d.ts +3 -0
- package/dist/agents/adapters/mcp/definitions.js +83 -1
- package/dist/agents/adapters/mcp/handlers/governance.js +3 -2
- package/dist/agents/adapters/mcp/handlers/memory.js +8 -3
- package/dist/agents/adapters/mcp/handlers/orchestration.js +27 -11
- package/dist/agents/adapters/mcp/helpers.d.ts +0 -2
- package/dist/agents/adapters/mcp/helpers.js +0 -7
- package/dist/agents/adapters/mcp/util/token-engine.d.ts +2 -0
- package/dist/agents/adapters/mcp/util/token-engine.js +7 -0
- package/dist/agents/adapters/mcp.js +13 -12
- package/dist/cli/hook.d.ts +9 -1
- package/dist/cli/hook.js +11 -2
- package/dist/cli/install-wizard.js +4 -1
- package/dist/cli.js +100 -1
- package/dist/daemon/server.js +7 -8
- package/dist/dashboard/app/index.html +20 -1
- package/dist/dashboard/app/main.js +14 -0
- package/dist/dashboard/app/state.js +7 -0
- package/dist/dashboard/app/styles/tokens.css +5 -9
- package/dist/dashboard/app/views/board.js +186 -0
- package/dist/dashboard/server.js +6 -1
- package/dist/dashboard/stream/sse-broker.js +21 -15
- package/dist/engines/client-bootstrap.js +27 -3
- package/dist/engines/context-assembler.d.ts +1 -1
- package/dist/engines/context-assembler.js +12 -7
- package/dist/engines/event-bus.d.ts +29 -1
- package/dist/engines/generated-specialist-loader.js +25 -3
- package/dist/engines/machine-efficiency/types.js +5 -1
- package/dist/engines/memory.d.ts +6 -0
- package/dist/engines/memory.js +32 -16
- package/dist/engines/orchestrator.js +54 -25
- package/dist/engines/token-supremacy.d.ts +2 -2
- package/dist/engines/token-supremacy.js +4 -4
- package/dist/engines/workspace-resolver.js +6 -10
- package/dist/index.js +62 -35
- package/dist/kernel/context/service.d.ts +2 -2
- package/dist/kernel/index.d.ts +2 -2
- package/dist/phantom/runtime.d.ts +1 -1
- package/dist/phantom/runtime.js +81 -5
- package/package.json +4 -3
- package/dist/agents/adapters/mcp/util/auto-memory.d.ts +0 -15
- 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
|
-
|
|
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,
|
|
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
|
-
//
|
|
370
|
+
// Surface hygiene-sweep on the SSE feed; do not persist as a memory.
|
|
372
371
|
try {
|
|
373
|
-
|
|
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,
|
|
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
|
-
//
|
|
140
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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();
|
|
@@ -830,21 +830,22 @@ export class MCPAdapter {
|
|
|
830
830
|
: { error: envelope.error.message }),
|
|
831
831
|
});
|
|
832
832
|
this.telemetry.observeSuccessfulToolCall(toolName, args);
|
|
833
|
-
//
|
|
834
|
-
//
|
|
835
|
-
//
|
|
836
|
-
//
|
|
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
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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);
|
package/dist/cli/hook.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/daemon/server.js
CHANGED
|
@@ -185,14 +185,13 @@ export class NexusDaemonServer {
|
|
|
185
185
|
},
|
|
186
186
|
});
|
|
187
187
|
await this.nexus.start();
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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(); });
|