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.
- package/README.md +0 -8
- package/dist/agents/adapters/mcp/definitions.d.ts +0 -19
- package/dist/agents/adapters/mcp/definitions.js +1 -99
- package/dist/agents/adapters/mcp/dispatch.js +0 -6
- package/dist/agents/adapters/mcp/handlers/governance.js +4 -4
- package/dist/agents/adapters/mcp/handlers/orchestration.js +16 -42
- package/dist/agents/adapters/mcp/helpers.d.ts +4 -2
- package/dist/agents/adapters/mcp/helpers.js +13 -4
- package/dist/architects/bootstrap.d.ts +6 -0
- package/dist/architects/bootstrap.js +1 -1
- package/dist/cli/cleanup.d.ts +25 -0
- package/dist/cli/cleanup.js +114 -0
- package/dist/cli/doctor-storage.d.ts +19 -0
- package/dist/cli/doctor-storage.js +78 -0
- package/dist/cli/install-wizard.js +31 -0
- package/dist/cli/reset.d.ts +8 -8
- package/dist/cli/reset.js +37 -68
- package/dist/cli/uninstall.d.ts +46 -0
- package/dist/cli/uninstall.js +177 -0
- package/dist/cli.js +61 -33
- package/dist/dashboard/app/views/memory.js +102 -16
- package/dist/dashboard/routes/architects.js +10 -2
- package/dist/dashboard/routes/assets.js +43 -3
- package/dist/dashboard/routes/governance.js +115 -2
- package/dist/dashboard/routes/graph.js +23 -2
- package/dist/dashboard/routes/health.js +2 -1
- package/dist/dashboard/routes/license.js +43 -4
- package/dist/dashboard/routes/memory.js +66 -5
- package/dist/dashboard/server.js +0 -13
- package/dist/dashboard/types.d.ts +0 -2
- package/dist/engines/memory/store.js +0 -2
- package/dist/engines/memory.js +11 -11
- package/dist/engines/orchestrator.d.ts +1 -1
- package/dist/engines/orchestrator.js +16 -31
- package/dist/engines/runtime-hygiene.d.ts +6 -0
- package/dist/engines/runtime-hygiene.js +51 -0
- package/dist/index.js +4 -0
- package/dist/install/fs-purge.d.ts +65 -0
- package/dist/install/fs-purge.js +304 -0
- package/dist/install/manifest.d.ts +36 -0
- package/dist/install/manifest.js +107 -0
- package/dist/install/process-cleanup.d.ts +21 -0
- package/dist/install/process-cleanup.js +161 -0
- package/dist/install/registration.d.ts +41 -0
- package/dist/install/registration.js +258 -0
- package/dist/install/state-locator.d.ts +32 -0
- package/dist/install/state-locator.js +115 -0
- package/dist/synapse/bootstrap.d.ts +6 -0
- package/dist/synapse/bootstrap.js +1 -1
- package/dist/synapse/config.js +5 -1
- 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
|
-
|
|
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 '../../../../
|
|
9
|
-
import { formatReadingPlan
|
|
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 } =
|
|
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 '../../../../
|
|
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
|
|
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
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
369
|
-
|
|
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
|
|
586
|
-
const lastSession = await
|
|
587
|
-
const delta = lastSession ?
|
|
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
|
|
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
|
|
27
|
-
export
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
export
|
|
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;
|