nexus-prime 6.0.0 → 6.1.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/dist/agents/adapters/ide-compat.d.ts +3 -0
- package/dist/agents/adapters/ide-compat.js +54 -1
- package/dist/agents/adapters/mcp/dispatch.js +22 -7
- package/dist/agents/adapters/mcp/envelope.d.ts +50 -0
- package/dist/agents/adapters/mcp/envelope.js +45 -0
- package/dist/agents/adapters/mcp/handlers/discovery.js +4 -0
- package/dist/agents/adapters/mcp/handlers/governance.js +32 -0
- package/dist/agents/adapters/mcp/handlers/memory.js +8 -2
- package/dist/agents/adapters/mcp/handlers/orchestration.js +66 -37
- package/dist/agents/adapters/mcp/handlers/runtime.js +5 -1
- package/dist/agents/adapters/mcp/runHandler.d.ts +7 -34
- package/dist/agents/adapters/mcp/runHandler.js +46 -35
- package/dist/agents/adapters/mcp/tool-health.d.ts +17 -0
- package/dist/agents/adapters/mcp/tool-health.js +57 -0
- package/dist/agents/adapters/mcp/util/detect-caller.d.ts +14 -6
- package/dist/agents/adapters/mcp/util/detect-caller.js +74 -11
- package/dist/agents/adapters/mcp/util/require-runtime.d.ts +24 -0
- package/dist/agents/adapters/mcp/util/require-runtime.js +28 -0
- package/dist/agents/adapters/mcp/util/walk.d.ts +9 -2
- package/dist/agents/adapters/mcp/util/walk.js +27 -5
- package/dist/agents/adapters/mcp.js +60 -57
- package/dist/architects/db/schema.js +3 -0
- package/dist/architects/sentinel/patrol.d.ts +19 -0
- package/dist/architects/sentinel/patrol.js +22 -0
- package/dist/cli/hook.js +34 -2
- package/dist/cli/install-wizard.d.ts +16 -0
- package/dist/cli/install-wizard.js +260 -15
- package/dist/cli/reset.d.ts +30 -0
- package/dist/cli/reset.js +132 -0
- package/dist/cli.js +102 -13
- package/dist/daemon/lock.d.ts +19 -0
- package/dist/daemon/lock.js +70 -1
- package/dist/daemon/preflight.d.ts +23 -0
- package/dist/daemon/preflight.js +121 -0
- package/dist/daemon/server.d.ts +1 -0
- package/dist/daemon/server.js +50 -1
- package/dist/dashboard/app/api.js +75 -23
- package/dist/dashboard/app/index.html +74 -0
- package/dist/dashboard/app/main.js +38 -4
- package/dist/dashboard/app/sse.js +6 -0
- package/dist/dashboard/app/state.js +6 -0
- package/dist/dashboard/app/styles/animation.css +10 -0
- package/dist/dashboard/app/styles/authoring.css +199 -0
- package/dist/dashboard/app/styles/governance.css +121 -0
- package/dist/dashboard/app/styles/tokens.css +62 -5
- package/dist/dashboard/app/styles/trust.css +356 -0
- package/dist/dashboard/app/styles/workforce.css +4 -0
- package/dist/dashboard/app/views/authoring.js +295 -0
- package/dist/dashboard/app/views/board.js +37 -0
- package/dist/dashboard/app/views/federation.js +216 -0
- package/dist/dashboard/app/views/governance.js +317 -0
- package/dist/dashboard/app/views/knowledge.js +103 -2
- package/dist/dashboard/app/views/license.js +336 -0
- package/dist/dashboard/app/views/memory.js +30 -5
- package/dist/dashboard/app/views/repo.js +262 -0
- package/dist/dashboard/app/views/trust.js +296 -0
- package/dist/dashboard/app/views/workforce.js +259 -35
- package/dist/dashboard/app/widgets/error-banner.js +83 -0
- package/dist/dashboard/app/widgets/runtime-badge.js +46 -0
- package/dist/dashboard/routes/architects.d.ts +2 -0
- package/dist/dashboard/routes/architects.js +46 -0
- package/dist/dashboard/routes/authoring.d.ts +2 -0
- package/dist/dashboard/routes/authoring.js +193 -0
- package/dist/dashboard/routes/governance.js +104 -7
- package/dist/dashboard/routes/graph.d.ts +13 -0
- package/dist/dashboard/routes/graph.js +94 -0
- package/dist/dashboard/routes/health.js +5 -0
- package/dist/dashboard/routes/runtime.js +24 -50
- package/dist/dashboard/server.d.ts +7 -3
- package/dist/dashboard/server.js +133 -81
- package/dist/dashboard/stream/sse-broker.js +13 -0
- package/dist/dashboard/types.d.ts +15 -2
- package/dist/engines/benchmark-extended.js +2 -2
- package/dist/engines/byzantine-consensus.js +22 -0
- package/dist/engines/client-registry.d.ts +2 -0
- package/dist/engines/client-registry.js +25 -5
- package/dist/engines/code-review-graph-client.d.ts +13 -0
- package/dist/engines/code-review-graph-client.js +60 -0
- package/dist/engines/event-bus.d.ts +80 -1
- package/dist/engines/instruction-gateway.js +14 -14
- package/dist/engines/knowledge-fabric.d.ts +1 -1
- package/dist/engines/knowledge-fabric.js +4 -4
- package/dist/engines/machine-efficiency/pressure-detector.js +10 -4
- package/dist/engines/memory/entropy-decay.d.ts +19 -0
- package/dist/engines/memory/entropy-decay.js +19 -0
- package/dist/engines/memory/recall.d.ts +8 -0
- package/dist/engines/memory/recall.js +36 -0
- package/dist/engines/memory/schema.d.ts +21 -0
- package/dist/engines/memory/schema.js +127 -0
- package/dist/engines/memory/store.d.ts +18 -0
- package/dist/engines/memory/store.js +86 -0
- package/dist/engines/memory/tier-promotion.d.ts +21 -0
- package/dist/engines/memory/tier-promotion.js +14 -0
- package/dist/engines/memory-bridge.d.ts +1 -1
- package/dist/engines/memory-bridge.js +2 -2
- package/dist/engines/memory.d.ts +10 -1
- package/dist/engines/memory.js +45 -194
- package/dist/engines/middleware-pipeline.d.ts +3 -0
- package/dist/engines/middleware-pipeline.js +20 -0
- package/dist/engines/nexus-layer.d.ts +1 -1
- package/dist/engines/orchestrator/funnel.d.ts +49 -0
- package/dist/engines/orchestrator/funnel.js +82 -0
- package/dist/engines/orchestrator.js +144 -61
- package/dist/engines/perf-history.d.ts +33 -0
- package/dist/engines/perf-history.js +93 -0
- package/dist/engines/runtime-backends.d.ts +3 -3
- package/dist/engines/runtime-backends.js +3 -3
- package/dist/engines/schema/integrity.d.ts +23 -0
- package/dist/engines/schema/integrity.js +111 -0
- package/dist/engines/schema/migrate.d.ts +27 -0
- package/dist/engines/schema/migrate.js +90 -0
- package/dist/engines/telemetry.d.ts +28 -0
- package/dist/engines/telemetry.js +79 -0
- package/dist/engines/token-supremacy.d.ts +6 -11
- package/dist/engines/token-supremacy.js +49 -53
- package/dist/index.d.ts +11 -3
- package/dist/index.js +23 -4
- package/dist/invokers/cursor.d.ts +27 -0
- package/dist/invokers/cursor.js +163 -0
- package/dist/invokers/registry.js +3 -0
- package/dist/invokers/types.d.ts +1 -1
- package/dist/migrations/architects/v001_baseline.sql +4 -0
- package/dist/migrations/memory/v001_baseline.sql +4 -0
- package/dist/migrations/synapse/v001_baseline.sql +4 -0
- package/dist/phantom/index.js +1 -1
- package/dist/phantom/runtime/diff-preview.d.ts +9 -0
- package/dist/phantom/runtime/diff-preview.js +72 -0
- package/dist/phantom/runtime/ghost-pass.d.ts +5 -0
- package/dist/phantom/runtime/ghost-pass.js +7 -0
- package/dist/phantom/runtime/index.d.ts +5 -0
- package/dist/phantom/runtime/index.js +5 -0
- package/dist/phantom/runtime/worker.d.ts +57 -0
- package/dist/phantom/runtime/worker.js +193 -0
- package/dist/phantom/runtime/worktree.d.ts +69 -0
- package/dist/phantom/runtime/worktree.js +227 -0
- package/dist/phantom/runtime.d.ts +10 -12
- package/dist/phantom/runtime.js +66 -536
- package/dist/postinstall/cleanup.d.ts +16 -0
- package/dist/postinstall/cleanup.js +85 -0
- package/dist/synapse/db/schema.js +3 -0
- package/dist/synapse/mandate/pipeline.js +5 -0
- package/dist/verify-token-scoring.js +1 -1
- package/package.json +6 -6
- package/dist/dashboard/index.html +0 -3678
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Supported: VS Code (Claude Code ext), JetBrains, Cursor, Windsurf, Zed, Continue.dev, Aider, Cline, Codex
|
|
6
6
|
*/
|
|
7
7
|
export type IDEId = 'claude-code' | 'cursor' | 'windsurf' | 'continue' | 'cline' | 'zed' | 'codex';
|
|
8
|
+
export type CallerIDEId = 'claude-code' | 'cursor' | 'windsurf' | 'continue' | 'zed' | 'codex';
|
|
8
9
|
export interface McpConfigOutput {
|
|
9
10
|
/** Absolute path where the config should be written */
|
|
10
11
|
configPath: string | null;
|
|
@@ -13,6 +14,8 @@ export interface McpConfigOutput {
|
|
|
13
14
|
/** If true, attempt to merge into existing file rather than overwrite */
|
|
14
15
|
merge: boolean;
|
|
15
16
|
}
|
|
17
|
+
export declare function resetDetectedCallerIDECache(): void;
|
|
18
|
+
export declare function detectCallerIDE(): CallerIDEId | null;
|
|
16
19
|
/** Detect which IDEs are present in the workspace or home directory. */
|
|
17
20
|
export declare function detectInstalledIDEs(workspaceRoot: string): IDEId[];
|
|
18
21
|
/** Get the MCP config content and target path for a given IDE. */
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync } from 'fs';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
|
-
import { join } from 'path';
|
|
9
|
+
import { dirname, join, resolve } from 'path';
|
|
10
|
+
let callerIDECache;
|
|
10
11
|
/** Nexus Prime MCP server entry (stdio transport). */
|
|
11
12
|
function nexusMcpServerEntry(workspaceRoot) {
|
|
12
13
|
return {
|
|
@@ -15,6 +16,58 @@ function nexusMcpServerEntry(workspaceRoot) {
|
|
|
15
16
|
env: {},
|
|
16
17
|
};
|
|
17
18
|
}
|
|
19
|
+
function hasAnyEnvPrefix(prefix) {
|
|
20
|
+
return Object.keys(process.env).some((key) => key.startsWith(prefix));
|
|
21
|
+
}
|
|
22
|
+
function detectCallerIDEFromEnv() {
|
|
23
|
+
if (process.env.CLAUDECODE || process.env.CLAUDE_CODE || process.env.CLAUDE_SESSION_ID || process.env.CLAUDE_PROJECT_DIR) {
|
|
24
|
+
return 'claude-code';
|
|
25
|
+
}
|
|
26
|
+
if (process.env.CURSOR_TRACE_ID || hasAnyEnvPrefix('CURSOR_')) {
|
|
27
|
+
return 'cursor';
|
|
28
|
+
}
|
|
29
|
+
if (process.env.CODEX_HOME || hasAnyEnvPrefix('CODEX_')) {
|
|
30
|
+
return 'codex';
|
|
31
|
+
}
|
|
32
|
+
if (hasAnyEnvPrefix('WINDSURF_')) {
|
|
33
|
+
return 'windsurf';
|
|
34
|
+
}
|
|
35
|
+
if (hasAnyEnvPrefix('CONTINUE_')) {
|
|
36
|
+
return 'continue';
|
|
37
|
+
}
|
|
38
|
+
if (hasAnyEnvPrefix('ZED_')) {
|
|
39
|
+
return 'zed';
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function detectCallerIDEFromFilesystem(startDir) {
|
|
44
|
+
let current = resolve(startDir);
|
|
45
|
+
for (;;) {
|
|
46
|
+
if (existsSync(join(current, '.cursor')))
|
|
47
|
+
return 'cursor';
|
|
48
|
+
if (existsSync(join(current, '.continue')))
|
|
49
|
+
return 'continue';
|
|
50
|
+
if (existsSync(join(current, '.windsurf')))
|
|
51
|
+
return 'windsurf';
|
|
52
|
+
if (existsSync(join(current, '.zed')))
|
|
53
|
+
return 'zed';
|
|
54
|
+
const parent = dirname(current);
|
|
55
|
+
if (parent === current) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
current = parent;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function resetDetectedCallerIDECache() {
|
|
62
|
+
callerIDECache = undefined;
|
|
63
|
+
}
|
|
64
|
+
export function detectCallerIDE() {
|
|
65
|
+
if (callerIDECache !== undefined) {
|
|
66
|
+
return callerIDECache;
|
|
67
|
+
}
|
|
68
|
+
callerIDECache = detectCallerIDEFromEnv() ?? detectCallerIDEFromFilesystem(process.cwd());
|
|
69
|
+
return callerIDECache;
|
|
70
|
+
}
|
|
18
71
|
/** Detect which IDEs are present in the workspace or home directory. */
|
|
19
72
|
export function detectInstalledIDEs(workspaceRoot) {
|
|
20
73
|
const detected = [];
|
|
@@ -13,6 +13,8 @@ import { handleGovernanceGroup } from './handlers/governance.js';
|
|
|
13
13
|
import { handleRuntimeGroup } from './handlers/runtime.js';
|
|
14
14
|
import { handleDiscoveryGroup } from './handlers/discovery.js';
|
|
15
15
|
import { handleMiscGroup } from './handlers/misc.js';
|
|
16
|
+
import { nexusEventBus } from '../../../engines/event-bus.js';
|
|
17
|
+
import { recordToolInvocation } from './tool-health.js';
|
|
16
18
|
/**
|
|
17
19
|
* Route a tool call to the appropriate handler group.
|
|
18
20
|
*
|
|
@@ -22,11 +24,24 @@ import { handleMiscGroup } from './handlers/misc.js';
|
|
|
22
24
|
*/
|
|
23
25
|
export async function dispatchMcpToolCall(hctx, request, args, ctx) {
|
|
24
26
|
const toolName = String(request.params?.name ?? '');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
await
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
const startedAt = Date.now();
|
|
28
|
+
try {
|
|
29
|
+
// Try each group in priority order. Groups return undefined for unknown tools.
|
|
30
|
+
const result = (await handleOrchestrationGroup(toolName, hctx, request, args, ctx) ??
|
|
31
|
+
await handleMemoryGroup(toolName, hctx, request, args, ctx) ??
|
|
32
|
+
await handleGovernanceGroup(toolName, hctx, request, args, ctx) ??
|
|
33
|
+
await handleRuntimeGroup(toolName, hctx, request, args, ctx) ??
|
|
34
|
+
await handleDiscoveryGroup(toolName, hctx, request, args, ctx) ??
|
|
35
|
+
await handleMiscGroup(toolName, hctx, request, args, ctx));
|
|
36
|
+
const durationMs = Date.now() - startedAt;
|
|
37
|
+
recordToolInvocation(toolName, durationMs, 'ok');
|
|
38
|
+
nexusEventBus.emit('tool.invocation', { toolName, durationMs, status: 'ok' });
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
const durationMs = Date.now() - startedAt;
|
|
43
|
+
recordToolInvocation(toolName, durationMs, 'error');
|
|
44
|
+
nexusEventBus.emit('tool.invocation', { toolName, durationMs, status: 'error' });
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
32
47
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HandlerEnvelope — unified success/error shape for all MCP tool handlers.
|
|
3
|
+
*
|
|
4
|
+
* Lifted out of runHandler.ts so any module (middleware, handlers, tests,
|
|
5
|
+
* dashboard routes) can import the type without pulling in the full
|
|
6
|
+
* runHandler runtime. runHandler.ts re-exports these for back-compat.
|
|
7
|
+
*/
|
|
8
|
+
export type HandlerErrorCode = 'timeout' | 'sqlite-busy' | 'runtime-cold' | 'input-invalid' | 'nested-dispatch-refused' | 'internal';
|
|
9
|
+
export interface HandlerEnvelope<T = unknown> {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
data?: T;
|
|
12
|
+
error?: {
|
|
13
|
+
code: HandlerErrorCode;
|
|
14
|
+
message: string;
|
|
15
|
+
retriable: boolean;
|
|
16
|
+
hint?: string;
|
|
17
|
+
};
|
|
18
|
+
meta: {
|
|
19
|
+
toolName: string;
|
|
20
|
+
durationMs: number;
|
|
21
|
+
attempts: number;
|
|
22
|
+
runtimeHotAt: number | null;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface RunHandlerOptions {
|
|
26
|
+
/** Override the per-client default timeout. */
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
/** Max retries for transient errors (SQLite-busy, runtime-cold). Default 1. */
|
|
29
|
+
retries?: number;
|
|
30
|
+
/** Caller name for per-client timeout selection. */
|
|
31
|
+
callerName?: string;
|
|
32
|
+
/** Timestamp (ms) when the runtime became hot. Passed through into meta. */
|
|
33
|
+
runtimeHotAt?: number | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Convert a HandlerEnvelope into the MCP tool-call response shape.
|
|
37
|
+
*
|
|
38
|
+
* On success, if `envelope.data` is already a ToolResult
|
|
39
|
+
* (`{ content: Array<...> }`), it is returned directly so we never
|
|
40
|
+
* double-wrap handler output. Any other value is JSON-stringified into a
|
|
41
|
+
* single text content block.
|
|
42
|
+
*
|
|
43
|
+
* On error, a structured JSON error block is produced.
|
|
44
|
+
*/
|
|
45
|
+
export declare function envelopeToMcpResponse<T>(envelope: HandlerEnvelope<T>): {
|
|
46
|
+
content: Array<{
|
|
47
|
+
type: 'text';
|
|
48
|
+
text: string;
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HandlerEnvelope — unified success/error shape for all MCP tool handlers.
|
|
3
|
+
*
|
|
4
|
+
* Lifted out of runHandler.ts so any module (middleware, handlers, tests,
|
|
5
|
+
* dashboard routes) can import the type without pulling in the full
|
|
6
|
+
* runHandler runtime. runHandler.ts re-exports these for back-compat.
|
|
7
|
+
*/
|
|
8
|
+
// ─── Serialisation helper ─────────────────────────────────────────────────────
|
|
9
|
+
/**
|
|
10
|
+
* Convert a HandlerEnvelope into the MCP tool-call response shape.
|
|
11
|
+
*
|
|
12
|
+
* On success, if `envelope.data` is already a ToolResult
|
|
13
|
+
* (`{ content: Array<...> }`), it is returned directly so we never
|
|
14
|
+
* double-wrap handler output. Any other value is JSON-stringified into a
|
|
15
|
+
* single text content block.
|
|
16
|
+
*
|
|
17
|
+
* On error, a structured JSON error block is produced.
|
|
18
|
+
*/
|
|
19
|
+
export function envelopeToMcpResponse(envelope) {
|
|
20
|
+
if (envelope.ok) {
|
|
21
|
+
const data = envelope.data;
|
|
22
|
+
// Pass-through: handler already returned a ToolResult-shaped object.
|
|
23
|
+
if (data !== null &&
|
|
24
|
+
data !== undefined &&
|
|
25
|
+
typeof data === 'object' &&
|
|
26
|
+
Array.isArray(data.content)) {
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
|
|
30
|
+
return { content: [{ type: 'text', text }] };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
content: [{
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: JSON.stringify({
|
|
36
|
+
status: 'error',
|
|
37
|
+
code: envelope.error.code,
|
|
38
|
+
message: envelope.error.message,
|
|
39
|
+
retriable: envelope.error.retriable,
|
|
40
|
+
hint: envelope.error.hint,
|
|
41
|
+
meta: envelope.meta,
|
|
42
|
+
}, null, 2),
|
|
43
|
+
}],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -5,8 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { formatBullets, formatJsonDetails, collectFolderFiles, } from '../helpers.js';
|
|
7
7
|
import { listExecutionPresets } from '../../../../engines/execution-presets.js';
|
|
8
|
+
import { requireRuntime } from '../util/require-runtime.js';
|
|
8
9
|
import * as path from 'path';
|
|
9
10
|
export async function handleDiscoveryGroup(toolName, hctx, request, args, ctx) {
|
|
11
|
+
const runtimeError = requireRuntime(hctx);
|
|
12
|
+
if (runtimeError)
|
|
13
|
+
return runtimeError;
|
|
10
14
|
switch (toolName) {
|
|
11
15
|
case 'nexus_list_execution_presets': {
|
|
12
16
|
const presets = listExecutionPresets();
|
|
@@ -17,9 +17,15 @@ import { ensureBootstrap } from '../../../../engines/client-bootstrap.js';
|
|
|
17
17
|
import { HybridRetriever } from '../../../../engines/hybrid-retriever.js';
|
|
18
18
|
import { GraphTraversalEngine } from '../../../../engines/graph-traversal.js';
|
|
19
19
|
import { fileURLToPath } from 'url';
|
|
20
|
+
import { requireRuntime } from '../util/require-runtime.js';
|
|
21
|
+
import { getAllPerfStats } from '../../../../engines/perf-history.js';
|
|
22
|
+
import { getTTVSnapshot } from '../../../../engines/telemetry.js';
|
|
20
23
|
const _gfile = fileURLToPath(import.meta.url);
|
|
21
24
|
const PROJECT_ROOT = path.resolve(path.dirname(_gfile), '..', '..', '..', '..', '..');
|
|
22
25
|
export async function handleGovernanceGroup(toolName, hctx, request, args, ctx) {
|
|
26
|
+
const runtimeError = requireRuntime(hctx);
|
|
27
|
+
if (runtimeError)
|
|
28
|
+
return runtimeError;
|
|
23
29
|
switch (toolName) {
|
|
24
30
|
case 'nexus_runtime_health': {
|
|
25
31
|
const envelope = hctx.nexusRef.getRuntimeHealthEnvelope();
|
|
@@ -78,6 +84,30 @@ export async function handleGovernanceGroup(toolName, hctx, request, args, ctx)
|
|
|
78
84
|
: []),
|
|
79
85
|
];
|
|
80
86
|
const allLive = deadSubsystems.length === 0;
|
|
87
|
+
// ── perfProfile: p50/p90/p99 per tool + TTV snapshot ─────────
|
|
88
|
+
const [perfStats, ttvSnap] = await Promise.all([
|
|
89
|
+
getAllPerfStats().catch(() => []),
|
|
90
|
+
Promise.resolve(getTTVSnapshot()),
|
|
91
|
+
]);
|
|
92
|
+
const perfProfile = {
|
|
93
|
+
tools: perfStats.map((s) => ({
|
|
94
|
+
tool: s.tool,
|
|
95
|
+
count: s.count,
|
|
96
|
+
p50ms: s.p50,
|
|
97
|
+
p90ms: s.p90,
|
|
98
|
+
p99ms: s.p99,
|
|
99
|
+
budget: s.budget ?? null,
|
|
100
|
+
slow: s.budget ? s.p99 > s.budget.fail : false,
|
|
101
|
+
})),
|
|
102
|
+
ttv: {
|
|
103
|
+
daemonStartAt: ttvSnap.daemonStartAt,
|
|
104
|
+
firstBootstrapMs: ttvSnap.firstBootstrapAt != null ? ttvSnap.firstBootstrapAt - ttvSnap.daemonStartAt : null,
|
|
105
|
+
firstMemoryMs: ttvSnap.firstMemoryAt != null ? ttvSnap.firstMemoryAt - ttvSnap.daemonStartAt : null,
|
|
106
|
+
firstInteractionMs: ttvSnap.firstInteractionAt != null ? ttvSnap.firstInteractionAt - ttvSnap.daemonStartAt : null,
|
|
107
|
+
firstMissionMs: ttvSnap.firstMissionCompleteAt != null ? ttvSnap.firstMissionCompleteAt - ttvSnap.daemonStartAt : null,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
const slowTools = perfProfile.tools.filter((t) => t.slow).map((t) => t.tool);
|
|
81
111
|
return {
|
|
82
112
|
content: [{
|
|
83
113
|
type: 'text',
|
|
@@ -94,6 +124,7 @@ export async function handleGovernanceGroup(toolName, hctx, request, args, ctx)
|
|
|
94
124
|
`Workspace: ${envelope.workspace.source}`,
|
|
95
125
|
`Issues: ${issues.length}`,
|
|
96
126
|
`Ngram index: ${ngramOversized ? 'oversized' : 'ok'} (${Math.round(ngramSizeBytes / 1024 / 1024)}MB)`,
|
|
127
|
+
...(slowTools.length > 0 ? [`\u26A0\uFE0F Slow tools (p99 > budget): ${slowTools.join(', ')}`] : []),
|
|
97
128
|
]),
|
|
98
129
|
issues.length > 0 ? formatBullets(issues.map((issue) => `\u26A0\uFE0F ${issue}`)) : '',
|
|
99
130
|
(() => {
|
|
@@ -115,6 +146,7 @@ export async function handleGovernanceGroup(toolName, hctx, request, args, ctx)
|
|
|
115
146
|
oversized: ngramOversized,
|
|
116
147
|
},
|
|
117
148
|
securityAudit: getSharedAuditLog().getSummary(),
|
|
149
|
+
perfProfile,
|
|
118
150
|
}),
|
|
119
151
|
].filter(Boolean).join('\n\n'),
|
|
120
152
|
}],
|
|
@@ -8,7 +8,12 @@ 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
10
|
import { storeToolSummary } from '../util/auto-memory.js';
|
|
11
|
+
import { requireRuntime } from '../util/require-runtime.js';
|
|
12
|
+
import { recordFirstMemory } from '../../../../engines/telemetry.js';
|
|
11
13
|
export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
|
|
14
|
+
const runtimeError = requireRuntime(hctx);
|
|
15
|
+
if (runtimeError)
|
|
16
|
+
return runtimeError;
|
|
12
17
|
switch (toolName) {
|
|
13
18
|
case 'nexus_store_memory': {
|
|
14
19
|
const content = String(request.params.arguments?.content ?? '');
|
|
@@ -19,6 +24,7 @@ export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
|
|
|
19
24
|
const verbatim = Boolean(request.params.arguments?.verbatim ?? false);
|
|
20
25
|
const id = hctx.nexusRef.storeMemory(content, priority, tags, undefined, undefined, { verbatim });
|
|
21
26
|
hctx.telemetry.recordStore();
|
|
27
|
+
void recordFirstMemory().catch(() => { });
|
|
22
28
|
hctx.sessionDNA.recordMemoryStore();
|
|
23
29
|
const nudge = hctx.telemetry.planningNudge('store', { priority });
|
|
24
30
|
if (ctx) {
|
|
@@ -170,7 +176,7 @@ export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
|
|
|
170
176
|
};
|
|
171
177
|
}
|
|
172
178
|
case 'nexus_memory_import': {
|
|
173
|
-
const result = hctx.nexusRef.importMemoryBundle({
|
|
179
|
+
const result = await hctx.nexusRef.importMemoryBundle({
|
|
174
180
|
path: request.params.arguments?.path ? String(request.params.arguments.path) : undefined,
|
|
175
181
|
bundle: request.params.arguments?.bundle,
|
|
176
182
|
});
|
|
@@ -232,7 +238,7 @@ export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
|
|
|
232
238
|
]);
|
|
233
239
|
}
|
|
234
240
|
else if (action === 'syncFrom') {
|
|
235
|
-
const result = bridge.syncFrom(sourceDir || bridge.getBridgeDir());
|
|
241
|
+
const result = await bridge.syncFrom(sourceDir || bridge.getBridgeDir());
|
|
236
242
|
detail = formatBullets([
|
|
237
243
|
`Success: ${result.success}`,
|
|
238
244
|
`Imported: ${result.imported}`,
|
|
@@ -12,12 +12,18 @@ 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';
|
|
15
|
-
import { promises as fsPromises
|
|
15
|
+
import { promises as fsPromises } from 'fs';
|
|
16
16
|
import { getSharedLicenseManager } from '../../../../licensing/index.js';
|
|
17
17
|
import { SessionDNAManager } from '../../../../engines/session-dna.js';
|
|
18
18
|
import { getSharedTelemetry } from '../../../../engines/telemetry-remote.js';
|
|
19
19
|
import { storeToolSummary } from '../util/auto-memory.js';
|
|
20
|
+
import { requireRuntime } from '../util/require-runtime.js';
|
|
21
|
+
import { ensureCrGraphBuilt } from '../../../../engines/code-review-graph-client.js';
|
|
22
|
+
import { recordFirstBootstrap } from '../../../../engines/telemetry.js';
|
|
20
23
|
export async function handleOrchestrationGroup(toolName, hctx, request, args, ctx) {
|
|
24
|
+
const runtimeError = requireRuntime(hctx);
|
|
25
|
+
if (runtimeError)
|
|
26
|
+
return runtimeError;
|
|
21
27
|
// Variables computed in the original handleToolCall preamble and used by these cases
|
|
22
28
|
const detailLevel = normalizeDetailLevel(args.detailLevel);
|
|
23
29
|
const requestedIntent = normalizeResponseIntent(args.intent);
|
|
@@ -117,7 +123,9 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
117
123
|
remoteTelemetry.trackTokenSavings(Number(bootstrap.tokenOptimization.planMetrics.savings));
|
|
118
124
|
}
|
|
119
125
|
}
|
|
120
|
-
catch {
|
|
126
|
+
catch (err) {
|
|
127
|
+
nexusEventBus.emit('mcp.handler.best-effort-failed', { toolName, stage: 'track-session-start', error: err instanceof Error ? err.message : String(err) });
|
|
128
|
+
}
|
|
121
129
|
if (ctx) {
|
|
122
130
|
ctx.meta.tokenSavings = Number(bootstrap.tokenOptimization?.planMetrics?.savings ?? 0);
|
|
123
131
|
ctx.meta.projectMemoryBootstrapCount = Number(bootstrap.projectMemoryBootstrap?.count ?? 0);
|
|
@@ -133,7 +141,20 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
133
141
|
try {
|
|
134
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}.`);
|
|
135
143
|
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
nexusEventBus.emit('mcp.handler.best-effort-failed', { toolName, stage: 'persist-session-summary', error: err instanceof Error ? err.message : String(err) });
|
|
146
|
+
}
|
|
147
|
+
// Non-blocking: ensure cr-graph is fresh after bootstrap. Fires after the handler
|
|
148
|
+
// returns so the response is not delayed. Any failure is silently swallowed.
|
|
149
|
+
try {
|
|
150
|
+
const crRepoRoot = hctx.nexusRef.getWorkspaceContext().repoRoot;
|
|
151
|
+
if (crRepoRoot) {
|
|
152
|
+
setImmediate(() => { void ensureCrGraphBuilt(crRepoRoot); });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
136
155
|
catch { /* best-effort */ }
|
|
156
|
+
// TTV telemetry: record first successful bootstrap (fire-and-forget).
|
|
157
|
+
void recordFirstBootstrap().catch(() => { });
|
|
137
158
|
const timings = {
|
|
138
159
|
totalMs: Date.now() - callStartedAt,
|
|
139
160
|
};
|
|
@@ -232,28 +253,11 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
232
253
|
formatBullets([
|
|
233
254
|
`Workspace: ${workspace.repoName} (${workspace.workspaceSource})`,
|
|
234
255
|
`Client: ${bootstrap.client?.displayName || bootstrap.client?.clientId || 'unknown'} (${bootstrap.client?.state || 'unknown'})`,
|
|
235
|
-
`Bootstrap depth: ${bootstrap.depth || bootstrapDepth}`,
|
|
236
256
|
`Memory recall: ${bootstrap.memoryRecall?.count ?? 0} result(s)`,
|
|
237
257
|
`Memory stats: prefrontal ${bootstrap.memoryStats?.prefrontal ?? 0} · hippocampus ${bootstrap.memoryStats?.hippocampus ?? 0} · cortex ${bootstrap.memoryStats?.cortex ?? 0}`,
|
|
238
258
|
`Recommended next step: ${bootstrap.recommendedNextStep || 'nexus_orchestrate'}`,
|
|
239
|
-
`Execution mode: ${bootstrap.recommendedExecutionMode || 'autonomous'}`,
|
|
240
259
|
`Token optimization: ${bootstrap.tokenOptimization?.autoApplied ? `auto-applied — saved ${Number(bootstrap.tokenOptimization?.planMetrics?.savings ?? 0).toLocaleString()} tokens (${bootstrap.tokenOptimization?.planMetrics?.pct ?? 0}% reduction)` : (bootstrap.tokenOptimization?.required ? 'required before broad reading' : 'not required yet')}`,
|
|
241
260
|
`Catalog health: ${bootstrap.catalogHealth?.overall || 'unknown'} · selected ${bootstrap.artifactSelectionAudit?.selected?.length || 0}`,
|
|
242
|
-
`Shortlist: ${bootstrap.shortlist?.skills?.slice(0, 3).join(', ') || 'none'} (skills), ${bootstrap.shortlist?.specialists?.slice(0, 3).join(', ') || 'none'} (specialists)`,
|
|
243
|
-
`Knowledge fabric: ${bootstrap.sourceMixRecommendation?.dominantSource || bootstrap.knowledgeFabric?.dominantSource || 'awaiting source mix'}`,
|
|
244
|
-
`RAG: ${bootstrap.ragCandidateStatus?.attachedCollections || 0} attached · ${bootstrap.ragCandidateStatus?.retrievedChunks || 0} retrieved`,
|
|
245
|
-
`Task graph: ${bootstrap.taskGraphPreview?.phases?.length || 0} phases · ${bootstrap.taskGraphPreview?.independentBranches || 0} branches`,
|
|
246
|
-
`Worker plan: ${bootstrap.workerPlanPreview?.totalWorkers || 0} workers planned`,
|
|
247
|
-
bootstrap.needsDeepBootstrap ? 'Deep preparation: still recommended before risky or cross-file work' : 'Deep preparation: not currently required',
|
|
248
|
-
`Payload ref: ${workspace.stateKey} · ${detailLevel}`,
|
|
249
|
-
`Session summary: ${bootstrap.sessionSummaryBootstrap?.savedTokens ? `reused ${Number(bootstrap.sessionSummaryBootstrap.savedTokens || 0).toLocaleString()} tokens from the previous visit` : 'none available yet'}`,
|
|
250
|
-
bootstrap.projectMemoryBootstrap?.count
|
|
251
|
-
? `Project memory: recovered ${bootstrap.projectMemoryBootstrap.count} prior project memories`
|
|
252
|
-
: 'Project memory: no prior project memories recovered',
|
|
253
|
-
bootstrap.autoGhostPass?.applied
|
|
254
|
-
? `Auto ghost-pass: ${bootstrap.autoGhostPass.riskAreas.length} risk area(s) · ${bootstrap.autoGhostPass.workerApproaches} approach(es)`
|
|
255
|
-
: `Auto ghost-pass: skipped`,
|
|
256
|
-
`Bootstrap status: ${bootstrap.clientBootstrapStatus?.clients?.length || 0} client manifests tracked`,
|
|
257
261
|
(() => {
|
|
258
262
|
try {
|
|
259
263
|
const _lic = getSharedLicenseManager().getStatus();
|
|
@@ -266,11 +270,30 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
266
270
|
return '';
|
|
267
271
|
}
|
|
268
272
|
})(),
|
|
273
|
+
...(detailLevel === 'debug' ? [
|
|
274
|
+
`Bootstrap depth: ${bootstrap.depth || bootstrapDepth}`,
|
|
275
|
+
`Execution mode: ${bootstrap.recommendedExecutionMode || 'autonomous'}`,
|
|
276
|
+
`Shortlist: ${bootstrap.shortlist?.skills?.slice(0, 3).join(', ') || 'none'} (skills), ${bootstrap.shortlist?.specialists?.slice(0, 3).join(', ') || 'none'} (specialists)`,
|
|
277
|
+
`Knowledge fabric: ${bootstrap.sourceMixRecommendation?.dominantSource || bootstrap.knowledgeFabric?.dominantSource || 'awaiting source mix'}`,
|
|
278
|
+
`RAG: ${bootstrap.ragCandidateStatus?.attachedCollections || 0} attached · ${bootstrap.ragCandidateStatus?.retrievedChunks || 0} retrieved`,
|
|
279
|
+
`Task graph: ${bootstrap.taskGraphPreview?.phases?.length || 0} phases · ${bootstrap.taskGraphPreview?.independentBranches || 0} branches`,
|
|
280
|
+
`Worker plan: ${bootstrap.workerPlanPreview?.totalWorkers || 0} workers planned`,
|
|
281
|
+
bootstrap.needsDeepBootstrap ? 'Deep preparation: still recommended before risky or cross-file work' : 'Deep preparation: not currently required',
|
|
282
|
+
`Payload ref: ${workspace.stateKey} · ${detailLevel}`,
|
|
283
|
+
`Session summary: ${bootstrap.sessionSummaryBootstrap?.savedTokens ? `reused ${Number(bootstrap.sessionSummaryBootstrap.savedTokens || 0).toLocaleString()} tokens from the previous visit` : 'none available yet'}`,
|
|
284
|
+
bootstrap.projectMemoryBootstrap?.count
|
|
285
|
+
? `Project memory: recovered ${bootstrap.projectMemoryBootstrap.count} prior project memories`
|
|
286
|
+
: 'Project memory: no prior project memories recovered',
|
|
287
|
+
bootstrap.autoGhostPass?.applied
|
|
288
|
+
? `Auto ghost-pass: ${bootstrap.autoGhostPass.riskAreas.length} risk area(s) · ${bootstrap.autoGhostPass.workerApproaches} approach(es)`
|
|
289
|
+
: `Auto ghost-pass: skipped`,
|
|
290
|
+
`Bootstrap status: ${bootstrap.clientBootstrapStatus?.clients?.length || 0} client manifests tracked`,
|
|
291
|
+
] : []),
|
|
269
292
|
]),
|
|
270
293
|
detailLevel === 'debug' && bootstrap.tokenOptimization?.autoApplied && bootstrap.tokenOptimization?.plan
|
|
271
294
|
? `Auto token plan\n\`\`\`txt\n${bootstrap.tokenOptimization.plan}\n\`\`\``
|
|
272
295
|
: '',
|
|
273
|
-
formatJsonDetails('Structured details', payload),
|
|
296
|
+
detailLevel === 'debug' ? formatJsonDetails('Structured details', payload) : '',
|
|
274
297
|
hctx.formatProtocolChecklist(),
|
|
275
298
|
].filter(Boolean).join('\n\n'),
|
|
276
299
|
}],
|
|
@@ -350,7 +373,9 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
350
373
|
const modifiedFiles = execution.workerResults.flatMap((w) => w.modifiedFiles);
|
|
351
374
|
hctx.nexusRef.storeMemory(`Run ${execution.runId} ${execution.state}: ${summarizeExecution(execution)}. Workers: ${verifiedWorkers}/${execution.workerResults.length} verified. Files modified: ${modifiedFiles.slice(0, 5).join(', ') || 'none'}.`, 0.34, ['#run-summary', '#auto', '#system-hidden']);
|
|
352
375
|
}
|
|
353
|
-
catch {
|
|
376
|
+
catch (err) {
|
|
377
|
+
nexusEventBus.emit('mcp.handler.best-effort-failed', { toolName, stage: 'auto-memory-store', error: err instanceof Error ? err.message : String(err) });
|
|
378
|
+
}
|
|
354
379
|
execution.activeSkills.forEach((skill) => hctx.sessionDNA.recordSkill(skill.name));
|
|
355
380
|
execution.workerResults.forEach((result) => {
|
|
356
381
|
result.modifiedFiles.forEach((file) => hctx.sessionDNA.recordFileModified(file));
|
|
@@ -440,21 +465,23 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
440
465
|
`Run ID: ${execution.runId}`,
|
|
441
466
|
`Summary: ${summarizeExecution(execution)}`,
|
|
442
467
|
`Crew: ${execution.plannerState?.selectedCrew?.name || 'baseline path'}`,
|
|
443
|
-
`Specialists: ${execution.plannerState?.selectedSpecialists.map((specialist) => specialist.name).slice(0, 4).join(', ') || 'none selected'}`,
|
|
444
|
-
`Assets: ${(execution.activeSkills || []).length} skills · ${(execution.activeWorkflows || []).length} workflows · ${(runtimeUsage.artifactSelectionAudit?.selected?.length || 0)} audited selections`,
|
|
445
|
-
`Task graph: ${runtimeUsage.taskGraph?.phases?.length || execution.taskGraph?.phases?.length || 0} phases · ${runtimeUsage.workerPlan?.totalWorkers || execution.workerPlan?.totalWorkers || 0} workers`,
|
|
446
|
-
`Catalog health: ${runtimeUsage.catalogHealth?.overall || 'unknown'}`,
|
|
447
|
-
preset ? `Preset: ${preset.name} (${preset.id})` : null,
|
|
448
468
|
`Verification: ${verifiedWorkers}/${execution.workerResults.length} worker(s) verified`,
|
|
449
|
-
`
|
|
450
|
-
`Tokens: saved ${Number(execution.tokenTelemetry?.savedTokens || 0).toLocaleString()} · compression ${Number(execution.tokenTelemetry?.compressionPct || 0)}% · dominant ${runtimeUsage.sourceAwareTokenBudget?.dominantSource || execution.knowledgeFabric?.sourceMix?.dominantSource || 'repo'}`,
|
|
469
|
+
`Tokens: saved ${Number(execution.tokenTelemetry?.savedTokens || 0).toLocaleString()} · compression ${Number(execution.tokenTelemetry?.compressionPct || 0)}%`,
|
|
451
470
|
autoTokenApplyNote || null,
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
471
|
+
...(detailLevel === 'debug' ? [
|
|
472
|
+
`Specialists: ${execution.plannerState?.selectedSpecialists.map((specialist) => specialist.name).slice(0, 4).join(', ') || 'none selected'}`,
|
|
473
|
+
`Assets: ${(execution.activeSkills || []).length} skills · ${(execution.activeWorkflows || []).length} workflows · ${(runtimeUsage.artifactSelectionAudit?.selected?.length || 0)} audited selections`,
|
|
474
|
+
`Task graph: ${runtimeUsage.taskGraph?.phases?.length || execution.taskGraph?.phases?.length || 0} phases · ${runtimeUsage.workerPlan?.totalWorkers || execution.workerPlan?.totalWorkers || 0} workers`,
|
|
475
|
+
`Catalog health: ${runtimeUsage.catalogHealth?.overall || 'unknown'}`,
|
|
476
|
+
preset ? `Preset: ${preset.name} (${preset.id})` : null,
|
|
477
|
+
`Worktrees: ${runtimeUsage.worktreeHealth?.overall || 'unknown'} · repaired ${runtimeUsage.worktreeHealth?.repairedEntries || 0} · broken ${runtimeUsage.worktreeHealth?.brokenEntries || 0}`,
|
|
478
|
+
`RAG: ${(runtimeUsage.ragUsageSummary?.attachedCollections || execution.knowledgeFabric?.rag.attachedCollections.length || 0)} attached · ${(runtimeUsage.ragUsageSummary?.retrievedChunks || execution.knowledgeFabric?.rag.hits.length || 0)} retrieved`,
|
|
479
|
+
`Memory scopes: ${Object.entries(runtimeUsage.memoryScopeUsage?.byScope || execution.memoryScopeUsage?.byScope || {}).map(([scope, count]) => `${scope}:${count}`).join(' · ') || 'awaiting shared/session reads'}`,
|
|
480
|
+
`Payload ref: ${workspace.stateKey} · ${detailLevel}`,
|
|
481
|
+
] : []),
|
|
455
482
|
]),
|
|
456
483
|
detailLevel === 'debug' && execution.result ? `Result\n\`\`\`\n${execution.result}\n\`\`\`` : `Result preview\n\`\`\`\n${truncateText(execution.result || summarizeExecution(execution), 900)}\n\`\`\``,
|
|
457
|
-
formatJsonDetails('Structured details', payload),
|
|
484
|
+
detailLevel === 'debug' ? formatJsonDetails('Structured details', payload) : '',
|
|
458
485
|
hctx.formatRemainingProtocolSteps(),
|
|
459
486
|
].filter(Boolean).join('\n\n'),
|
|
460
487
|
}],
|
|
@@ -509,7 +536,7 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
509
536
|
return { path: resolved, sizeBytes: 0 };
|
|
510
537
|
}
|
|
511
538
|
})));
|
|
512
|
-
const plan = getTokenEngine().plan(task, files);
|
|
539
|
+
const plan = await getTokenEngine().plan(task, files);
|
|
513
540
|
const formatted = formatReadingPlan(plan);
|
|
514
541
|
// Persist the decision
|
|
515
542
|
hctx.nexusRef.storeMemory(`Token plan for "${task.slice(0, 80)}": ` +
|
|
@@ -557,16 +584,16 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
557
584
|
const rawFiles = Array.isArray(request.params.arguments?.files)
|
|
558
585
|
? request.params.arguments.files.map(String)
|
|
559
586
|
: [];
|
|
560
|
-
const files = rawFiles.map(p => {
|
|
587
|
+
const files = await Promise.all(rawFiles.map(async (p) => {
|
|
561
588
|
const resolved = hctx.resolveToolPath(p, request.params.arguments ?? {});
|
|
562
589
|
try {
|
|
563
|
-
const stat =
|
|
590
|
+
const stat = await fsPromises.stat(resolved);
|
|
564
591
|
return { path: resolved, sizeBytes: stat.size, lastModified: stat.mtimeMs };
|
|
565
592
|
}
|
|
566
593
|
catch {
|
|
567
594
|
return { path: resolved, sizeBytes: 0 };
|
|
568
595
|
}
|
|
569
|
-
});
|
|
596
|
+
}));
|
|
570
597
|
const ghost = new GhostPass(hctx.getWorkspace(request.params.arguments ?? {}).repoRoot);
|
|
571
598
|
const report = await ghost.analyze(goal, files);
|
|
572
599
|
hctx.nexusRef.storeMemory(`Ghost pass for "${goal.slice(0, 80)}": ${report.riskAreas.length} risks identified.`, 0.6, ['#ghost-pass']);
|
|
@@ -650,7 +677,9 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
650
677
|
try {
|
|
651
678
|
storeToolSummary(hctx, 'nexus_spawn_workers', `Worker plan: "${goal.slice(0, 100)}" — ${workersCount} workers, ${rawFiles.length} files.`);
|
|
652
679
|
}
|
|
653
|
-
catch {
|
|
680
|
+
catch (err) {
|
|
681
|
+
nexusEventBus.emit('mcp.handler.best-effort-failed', { toolName, stage: 'worker-plan-summary', error: err instanceof Error ? err.message : String(err) });
|
|
682
|
+
}
|
|
654
683
|
const execution = await hctx.getRuntime().run(applyExecutionPreset({
|
|
655
684
|
goal,
|
|
656
685
|
files: rawFiles,
|
|
@@ -8,7 +8,11 @@ import { formatBullets, formatJsonDetails, } from '../helpers.js';
|
|
|
8
8
|
import { nexusEventBus } from '../../../../engines/event-bus.js';
|
|
9
9
|
import { getSharedLicenseManager, snapshotPCU, formatPCUStatus } from '../../../../licensing/index.js';
|
|
10
10
|
import { TokenAnalyticsEngine } from '../../../../engines/token-analytics.js';
|
|
11
|
+
import { requireRuntime } from '../util/require-runtime.js';
|
|
11
12
|
export async function handleRuntimeGroup(toolName, hctx, request, args, ctx) {
|
|
13
|
+
const runtimeError = requireRuntime(hctx);
|
|
14
|
+
if (runtimeError)
|
|
15
|
+
return runtimeError;
|
|
12
16
|
switch (toolName) {
|
|
13
17
|
case 'nexus_license_usage': {
|
|
14
18
|
const fmt = String(request.params.arguments?.format ?? 'summary');
|
|
@@ -220,7 +224,7 @@ export async function handleRuntimeGroup(toolName, hctx, request, args, ctx) {
|
|
|
220
224
|
}
|
|
221
225
|
case 'nexus_run_status': {
|
|
222
226
|
const runId = String(request.params.arguments?.runId ?? '');
|
|
223
|
-
const run = hctx.getRuntime().getRun(runId);
|
|
227
|
+
const run = await hctx.getRuntime().getRun(runId);
|
|
224
228
|
if (!run) {
|
|
225
229
|
return { content: [{ type: 'text', text: `❌ Run not found: ${runId}` }] };
|
|
226
230
|
}
|
|
@@ -8,34 +8,14 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Per-client timeouts (AI Lead refinement):
|
|
10
10
|
* Codex 90 s · Claude Code 60 s · Cursor 30 s · default 25 s
|
|
11
|
+
*
|
|
12
|
+
* Types and `envelopeToMcpResponse` are canonical in ./envelope.ts;
|
|
13
|
+
* re-exported from here for backward compatibility.
|
|
11
14
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
error?: {
|
|
17
|
-
code: HandlerErrorCode;
|
|
18
|
-
message: string;
|
|
19
|
-
retriable: boolean;
|
|
20
|
-
hint?: string;
|
|
21
|
-
};
|
|
22
|
-
meta: {
|
|
23
|
-
toolName: string;
|
|
24
|
-
durationMs: number;
|
|
25
|
-
attempts: number;
|
|
26
|
-
runtimeHotAt: number | null;
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
export interface RunHandlerOptions {
|
|
30
|
-
/** Override the per-client default timeout. */
|
|
31
|
-
timeoutMs?: number;
|
|
32
|
-
/** Max retries for transient errors (SQLite-busy, runtime-cold). Default 1. */
|
|
33
|
-
retries?: number;
|
|
34
|
-
/** Caller name for per-client timeout selection. */
|
|
35
|
-
callerName?: string;
|
|
36
|
-
/** Timestamp (ms) when the runtime became hot. Passed through into meta. */
|
|
37
|
-
runtimeHotAt?: number | null;
|
|
38
|
-
}
|
|
15
|
+
import type { RunHandlerOptions } from './envelope.js';
|
|
16
|
+
import type { HandlerEnvelope } from './envelope.js';
|
|
17
|
+
export type { HandlerErrorCode, HandlerEnvelope, RunHandlerOptions } from './envelope.js';
|
|
18
|
+
export { envelopeToMcpResponse } from './envelope.js';
|
|
39
19
|
/**
|
|
40
20
|
* Run a handler function with timeout, retry, and structured envelope.
|
|
41
21
|
*
|
|
@@ -46,10 +26,3 @@ export interface RunHandlerOptions {
|
|
|
46
26
|
* }, { callerName: hctx.adapterName });
|
|
47
27
|
*/
|
|
48
28
|
export declare function runHandler<T>(toolName: string, fn: () => Promise<T>, opts?: RunHandlerOptions): Promise<HandlerEnvelope<T>>;
|
|
49
|
-
/** Convert a HandlerEnvelope into the MCP tool-call response shape. */
|
|
50
|
-
export declare function envelopeToMcpResponse<T>(envelope: HandlerEnvelope<T>): {
|
|
51
|
-
content: Array<{
|
|
52
|
-
type: 'text';
|
|
53
|
-
text: string;
|
|
54
|
-
}>;
|
|
55
|
-
};
|