nexus-prime 7.9.13 → 7.9.15
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/mcp/stdio-buffer.d.ts +6 -0
- package/dist/agents/adapters/mcp/stdio-buffer.js +45 -0
- package/dist/agents/adapters/mcp.d.ts +2 -0
- package/dist/agents/adapters/mcp.js +60 -7
- package/dist/cli/install-wizard.js +19 -0
- package/dist/cli.js +31 -1
- package/dist/dashboard/app/index.html +8 -0
- package/dist/dashboard/app/main.js +7 -0
- package/dist/dashboard/app/state.js +5 -0
- package/dist/dashboard/app/styles/board.css +163 -2
- package/dist/dashboard/app/styles/context-log.css +167 -0
- package/dist/dashboard/app/styles/memory.css +63 -0
- package/dist/dashboard/app/styles/workforce.css +21 -0
- package/dist/dashboard/app/views/board.js +145 -7
- package/dist/dashboard/app/views/context-log.js +158 -0
- package/dist/dashboard/app/views/memory.js +87 -3
- package/dist/dashboard/app/views/workforce.js +22 -6
- package/dist/dashboard/routes/events.js +80 -3
- package/dist/dashboard/stream/sse-broker.js +25 -13
- package/dist/engines/client-bootstrap.js +66 -20
- package/dist/engines/code-review-graph-client.d.ts +11 -3
- package/dist/engines/code-review-graph-client.js +151 -24
- package/dist/engines/instruction-gateway.js +6 -0
- package/dist/engines/mcp-entrypoint.js +3 -1
- package/dist/engines/orchestrator/decision-spine.d.ts +170 -0
- package/dist/engines/orchestrator/decision-spine.js +424 -0
- package/dist/engines/orchestrator/selection-policy.d.ts +39 -0
- package/dist/engines/orchestrator/selection-policy.js +32 -0
- package/dist/engines/orchestrator.js +19 -33
- package/dist/phantom/runtime.d.ts +16 -0
- package/dist/phantom/runtime.js +158 -20
- package/package.json +2 -2
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PassThrough } from 'stream';
|
|
2
|
+
let primedStdioInput = null;
|
|
3
|
+
let primedStdioInputStarted = false;
|
|
4
|
+
let primedStdioInputReady = false;
|
|
5
|
+
let primedStdioInputEnded = false;
|
|
6
|
+
const primedStdioChunks = [];
|
|
7
|
+
export function primeMcpStdioInput() {
|
|
8
|
+
if (primedStdioInputStarted || process.stdin.isTTY)
|
|
9
|
+
return;
|
|
10
|
+
primedStdioInputStarted = true;
|
|
11
|
+
process.stdin.on('data', (chunk) => {
|
|
12
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
13
|
+
if (primedStdioInput && primedStdioInputReady) {
|
|
14
|
+
primedStdioInput.write(buffer);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
primedStdioChunks.push(buffer);
|
|
18
|
+
});
|
|
19
|
+
process.stdin.on('end', () => {
|
|
20
|
+
primedStdioInputEnded = true;
|
|
21
|
+
if (primedStdioInput && primedStdioInputReady)
|
|
22
|
+
primedStdioInput.end();
|
|
23
|
+
});
|
|
24
|
+
process.stdin.on('error', (error) => primedStdioInput?.destroy(error));
|
|
25
|
+
process.stdin.resume();
|
|
26
|
+
}
|
|
27
|
+
export function getMcpStdioInput() {
|
|
28
|
+
if (primedStdioInputStarted && !primedStdioInput) {
|
|
29
|
+
primedStdioInput = new PassThrough();
|
|
30
|
+
}
|
|
31
|
+
return primedStdioInput ?? process.stdin;
|
|
32
|
+
}
|
|
33
|
+
export function flushPrimedMcpStdioInput() {
|
|
34
|
+
if (!primedStdioInput)
|
|
35
|
+
return;
|
|
36
|
+
primedStdioInputReady = true;
|
|
37
|
+
for (const chunk of primedStdioChunks.splice(0)) {
|
|
38
|
+
primedStdioInput.write(chunk);
|
|
39
|
+
}
|
|
40
|
+
if (primedStdioInputEnded)
|
|
41
|
+
primedStdioInput.end();
|
|
42
|
+
}
|
|
43
|
+
if (process.argv[2] === 'mcp') {
|
|
44
|
+
primeMcpStdioInput();
|
|
45
|
+
}
|
|
@@ -20,6 +20,7 @@ export declare class MCPAdapter implements Adapter {
|
|
|
20
20
|
private currentTask;
|
|
21
21
|
private middlewarePipeline;
|
|
22
22
|
private scanCache;
|
|
23
|
+
private sessionLifecycleFinalized;
|
|
23
24
|
private sciFiMatrixLog;
|
|
24
25
|
private classifyToolCategory;
|
|
25
26
|
private sciFiToolHeader;
|
|
@@ -83,6 +84,7 @@ export declare class MCPAdapter implements Adapter {
|
|
|
83
84
|
private extractFileRefsFromArgs;
|
|
84
85
|
scanSourceFiles(cwd: string): Promise<string[]>;
|
|
85
86
|
connect(): Promise<void>;
|
|
87
|
+
private finalizeSessionLifecycle;
|
|
86
88
|
disconnect(): Promise<void>;
|
|
87
89
|
send(message: NetworkMessage): Promise<void>;
|
|
88
90
|
receive(message: NetworkMessage): void;
|
|
@@ -30,6 +30,7 @@ import { getFallbackRuntime, getFallbackOrchestrator, getGuardrailEngine, normal
|
|
|
30
30
|
import { dispatchMcpToolCall } from './mcp/dispatch.js';
|
|
31
31
|
import { LifecyclePolicy } from '../../engines/lifecycle-policy.js';
|
|
32
32
|
import { startWatchdog } from './mcp/watchdog.js';
|
|
33
|
+
import { getMcpStdioInput } from './mcp/stdio-buffer.js';
|
|
33
34
|
// Derive project root from this file's location (dist/agents/adapters/mcp.js → project root)
|
|
34
35
|
const __filename = fileURLToPath(import.meta.url);
|
|
35
36
|
const PROJECT_ROOT = path.resolve(path.dirname(__filename), '..', '..', '..');
|
|
@@ -159,6 +160,7 @@ export class MCPAdapter {
|
|
|
159
160
|
currentTask = '';
|
|
160
161
|
middlewarePipeline;
|
|
161
162
|
scanCache = new Map();
|
|
163
|
+
sessionLifecycleFinalized = false;
|
|
162
164
|
sciFiMatrixLog(title, metrics, intent) {
|
|
163
165
|
const width = 76;
|
|
164
166
|
console.error(`\n\x1b[36m╔═══ [ \x1b[37m\x1b[1mNEXUS PRIME · ORCHESTRATION MATRIX\x1b[0m\x1b[36m ] ${'═'.repeat(Math.max(0, width - 48))}╗\x1b[0m`);
|
|
@@ -875,6 +877,9 @@ export class MCPAdapter {
|
|
|
875
877
|
}
|
|
876
878
|
else {
|
|
877
879
|
this.telemetry.observeSuccessfulToolCall(toolName, args);
|
|
880
|
+
if (toolName === 'nexus_session_dna' && String(args.action ?? 'load') === 'generate') {
|
|
881
|
+
this.sessionLifecycleFinalized = true;
|
|
882
|
+
}
|
|
878
883
|
}
|
|
879
884
|
}
|
|
880
885
|
// Surface meaningful tool calls on the SSE feed; persist only high-signal outcomes.
|
|
@@ -1152,28 +1157,76 @@ export class MCPAdapter {
|
|
|
1152
1157
|
return scanSourceFilesUtil(cwd, this.scanCache);
|
|
1153
1158
|
}
|
|
1154
1159
|
async connect() {
|
|
1155
|
-
const transport = new StdioServerTransport();
|
|
1160
|
+
const transport = new StdioServerTransport(getMcpStdioInput(), process.stdout);
|
|
1156
1161
|
await this.server.connect(transport);
|
|
1157
1162
|
this.connected = true;
|
|
1158
1163
|
startWatchdog();
|
|
1159
1164
|
console.error('[MCP Adapter] Connected — runtime tools active');
|
|
1160
1165
|
}
|
|
1161
|
-
async
|
|
1162
|
-
|
|
1166
|
+
async finalizeSessionLifecycle(reason) {
|
|
1167
|
+
if (this.sessionLifecycleFinalized && reason !== 'explicit')
|
|
1168
|
+
return;
|
|
1169
|
+
const telemetry = this.telemetry.snapshot();
|
|
1170
|
+
try {
|
|
1171
|
+
const promoted = this.nexusRef?.runTierPromotion?.();
|
|
1172
|
+
if (promoted)
|
|
1173
|
+
nexusEventBus.emit('memory.tier.promoted', promoted);
|
|
1174
|
+
}
|
|
1175
|
+
catch (e) {
|
|
1176
|
+
nexusEventBus.emit('mcp.handler.failed', {
|
|
1177
|
+
toolName: 'nexus_session_dna.disconnect.tierPromotion',
|
|
1178
|
+
code: 'internal',
|
|
1179
|
+
attempts: 1,
|
|
1180
|
+
durationMs: 0,
|
|
1181
|
+
});
|
|
1182
|
+
console.error('[MCP Adapter] Failed to run Session DNA tier promotion:', e);
|
|
1183
|
+
}
|
|
1163
1184
|
try {
|
|
1164
|
-
const telemetry = this.telemetry.snapshot();
|
|
1165
1185
|
this.sessionDNA.syncFromTelemetry({
|
|
1166
1186
|
callCount: telemetry.callCount,
|
|
1167
1187
|
memoriesStored: telemetry.memoriesStored,
|
|
1168
1188
|
memoriesRecalled: telemetry.memoriesRecalled,
|
|
1169
1189
|
tokensOptimized: telemetry.tokensOptimized,
|
|
1170
1190
|
});
|
|
1171
|
-
this.sessionDNA.flush();
|
|
1172
|
-
|
|
1191
|
+
const dna = this.sessionDNA.flush();
|
|
1192
|
+
this.sessionLifecycleFinalized = true;
|
|
1193
|
+
try {
|
|
1194
|
+
const runtimeTokens = this.runtime?.getUsageSnapshot().tokens?.grossInputTokens ?? 0;
|
|
1195
|
+
this.getOrchestrator({}).persistSessionSummary(telemetry.tokensOptimized || runtimeTokens || 0);
|
|
1196
|
+
}
|
|
1197
|
+
catch (e) {
|
|
1198
|
+
nexusEventBus.emit('mcp.handler.failed', {
|
|
1199
|
+
toolName: 'nexus_session_dna.disconnect.persistSessionSummary',
|
|
1200
|
+
code: 'internal',
|
|
1201
|
+
attempts: 1,
|
|
1202
|
+
durationMs: 0,
|
|
1203
|
+
});
|
|
1204
|
+
console.error('[MCP Adapter] Failed to persist Session DNA summary:', e);
|
|
1205
|
+
}
|
|
1206
|
+
await getSharedTelemetry().trackSessionEnd(this.telemetry.elapsedMs(), {
|
|
1207
|
+
projectId: this.nexusRef?.getWorkspaceContext?.()?.repoRoot ?? '',
|
|
1208
|
+
tokensSaved: telemetry.tokensOptimized,
|
|
1209
|
+
memoriesCreated: telemetry.memoriesStored,
|
|
1210
|
+
memoriesRecalled: telemetry.memoriesRecalled,
|
|
1211
|
+
toolsUsed: telemetry.callCount,
|
|
1212
|
+
sessionId: dna.sessionId,
|
|
1213
|
+
}).catch((e) => {
|
|
1214
|
+
nexusEventBus.emit('mcp.handler.failed', {
|
|
1215
|
+
toolName: 'nexus_session_dna.disconnect.trackSessionEnd',
|
|
1216
|
+
code: 'internal',
|
|
1217
|
+
attempts: 1,
|
|
1218
|
+
durationMs: 0,
|
|
1219
|
+
});
|
|
1220
|
+
console.error('[MCP Adapter] Failed to track Session DNA session end:', e);
|
|
1221
|
+
});
|
|
1222
|
+
console.error(`[MCP Adapter] Session DNA finalized on ${reason}`);
|
|
1173
1223
|
}
|
|
1174
1224
|
catch (e) {
|
|
1175
|
-
console.error('[MCP Adapter] Failed to
|
|
1225
|
+
console.error('[MCP Adapter] Failed to finalize Session DNA:', e);
|
|
1176
1226
|
}
|
|
1227
|
+
}
|
|
1228
|
+
async disconnect() {
|
|
1229
|
+
await this.finalizeSessionLifecycle('disconnect');
|
|
1177
1230
|
await this.server.close();
|
|
1178
1231
|
this.connected = false;
|
|
1179
1232
|
console.error('[MCP Adapter] Disconnected');
|
|
@@ -159,6 +159,21 @@ function _writeClaudeCodeHooks(workspaceRoot) {
|
|
|
159
159
|
});
|
|
160
160
|
});
|
|
161
161
|
}
|
|
162
|
+
function _isManagedGraphPeer(server) {
|
|
163
|
+
if (!server || typeof server !== 'object' || Array.isArray(server))
|
|
164
|
+
return false;
|
|
165
|
+
const record = server;
|
|
166
|
+
const args = Array.isArray(record.args) ? record.args.map(String) : [];
|
|
167
|
+
return record.command === 'goatlas' && args.length === 1 && args[0] === 'mcp';
|
|
168
|
+
}
|
|
169
|
+
function _stripInternalGraphPeers(mcpServers) {
|
|
170
|
+
if (!mcpServers)
|
|
171
|
+
return;
|
|
172
|
+
delete mcpServers['code-review-graph'];
|
|
173
|
+
if (_isManagedGraphPeer(mcpServers['atlas'])) {
|
|
174
|
+
delete mcpServers['atlas'];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
162
177
|
/**
|
|
163
178
|
* Write workspace-local MCP config files for IDEs whose workspace directories
|
|
164
179
|
* already exist (`.vscode/`, `.cursor/`). Non-fatal; errors are swallowed.
|
|
@@ -181,6 +196,7 @@ function _writeWorkspaceLocalConfigs(callerIde, workspaceRoot) {
|
|
|
181
196
|
? settings.mcpServers
|
|
182
197
|
: {};
|
|
183
198
|
servers['nexus-prime'] = entry;
|
|
199
|
+
_stripInternalGraphPeers(servers);
|
|
184
200
|
settings.mcpServers = servers;
|
|
185
201
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
186
202
|
appendToManifest((m) => {
|
|
@@ -212,6 +228,7 @@ function _writeWorkspaceLocalConfigs(callerIde, workspaceRoot) {
|
|
|
212
228
|
? mcp.mcpServers
|
|
213
229
|
: {};
|
|
214
230
|
servers['nexus-prime'] = entry;
|
|
231
|
+
_stripInternalGraphPeers(servers);
|
|
215
232
|
mcp.mcpServers = servers;
|
|
216
233
|
writeFileSync(mcpPath, JSON.stringify(mcp, null, 2), 'utf8');
|
|
217
234
|
appendToManifest((m) => {
|
|
@@ -347,6 +364,7 @@ function mergeIntoExistingConfig(configPath, newContent, ide) {
|
|
|
347
364
|
...(existing.mcpServers ?? {}),
|
|
348
365
|
...(incoming.mcpServers ?? {}),
|
|
349
366
|
};
|
|
367
|
+
_stripInternalGraphPeers(existing.mcpServers);
|
|
350
368
|
return JSON.stringify(existing, null, 2);
|
|
351
369
|
}
|
|
352
370
|
if (ide === 'claude-code') {
|
|
@@ -362,6 +380,7 @@ function mergeIntoExistingConfig(configPath, newContent, ide) {
|
|
|
362
380
|
...(existing.mcpServers ?? {}),
|
|
363
381
|
...(incoming.mcpServers ?? {}),
|
|
364
382
|
};
|
|
383
|
+
_stripInternalGraphPeers(existing.mcpServers);
|
|
365
384
|
}
|
|
366
385
|
return JSON.stringify(existing, null, 2);
|
|
367
386
|
}
|
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Nexus Prime CLI
|
|
4
4
|
*/
|
|
5
5
|
import 'dotenv/config';
|
|
6
|
+
import { flushPrimedMcpStdioInput, primeMcpStdioInput } from './agents/adapters/mcp/stdio-buffer.js';
|
|
6
7
|
import { Command } from 'commander';
|
|
7
8
|
import { createNexusPrime } from './index.js';
|
|
8
9
|
import { TokenSupremacyEngine, formatReadingPlan } from './engines/token-supremacy.js';
|
|
@@ -196,9 +197,25 @@ function writeStandardMcpConfig(targetPath, workspaceRoot) {
|
|
|
196
197
|
const existing = readJson(targetPath);
|
|
197
198
|
existing.mcpServers = existing.mcpServers ?? {};
|
|
198
199
|
existing.mcpServers['nexus-prime'] = buildStandardMcpServerConfig(workspaceRoot);
|
|
200
|
+
delete existing.mcpServers['code-review-graph'];
|
|
201
|
+
if (isNexusManagedGraphPeer(existing.mcpServers['atlas'])) {
|
|
202
|
+
delete existing.mcpServers['atlas'];
|
|
203
|
+
}
|
|
199
204
|
ensureParentDir(targetPath);
|
|
200
205
|
writeFileSync(targetPath, JSON.stringify(existing, null, 2));
|
|
201
206
|
}
|
|
207
|
+
function isNexusManagedGraphPeer(server) {
|
|
208
|
+
if (!server || typeof server !== 'object' || Array.isArray(server))
|
|
209
|
+
return false;
|
|
210
|
+
const record = server;
|
|
211
|
+
const args = Array.isArray(record['args']) ? record['args'].map(String) : [];
|
|
212
|
+
return record['command'] === 'goatlas' && args.length === 1 && args[0] === 'mcp';
|
|
213
|
+
}
|
|
214
|
+
function jsonExposesInternalGraphPeer(parsed) {
|
|
215
|
+
const servers = parsed?.mcpServers ?? parsed?.mcp ?? {};
|
|
216
|
+
return Boolean(servers?.['code-review-graph']
|
|
217
|
+
|| isNexusManagedGraphPeer(servers?.['atlas']));
|
|
218
|
+
}
|
|
202
219
|
function writeOpencodeConfig(targetPath, workspaceRoot) {
|
|
203
220
|
const existing = readJson(targetPath);
|
|
204
221
|
const mcpConfig = buildStandardMcpServerConfig(workspaceRoot);
|
|
@@ -427,6 +444,15 @@ function buildInstructionFiles(clientId) {
|
|
|
427
444
|
content: artifact.content,
|
|
428
445
|
}));
|
|
429
446
|
}
|
|
447
|
+
function resolveClaudeDesktopConfigPath() {
|
|
448
|
+
if (process.platform === 'darwin') {
|
|
449
|
+
return join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
450
|
+
}
|
|
451
|
+
if (process.platform === 'win32') {
|
|
452
|
+
return join(process.env.APPDATA || join(homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
|
|
453
|
+
}
|
|
454
|
+
return join(homedir(), '.claude', 'claude_desktop_config.json');
|
|
455
|
+
}
|
|
430
456
|
function getSetupDefinition(clientId) {
|
|
431
457
|
const instructionFiles = buildInstructionFiles(clientId);
|
|
432
458
|
if (clientId === 'codex') {
|
|
@@ -457,7 +483,7 @@ function getSetupDefinition(clientId) {
|
|
|
457
483
|
return {
|
|
458
484
|
id: clientId,
|
|
459
485
|
label: 'Claude Desktop',
|
|
460
|
-
configPath:
|
|
486
|
+
configPath: resolveClaudeDesktopConfigPath(),
|
|
461
487
|
instructionFiles,
|
|
462
488
|
};
|
|
463
489
|
}
|
|
@@ -582,6 +608,8 @@ function hasExpectedConfig(definition) {
|
|
|
582
608
|
}
|
|
583
609
|
try {
|
|
584
610
|
const parsed = JSON.parse(readFileSync(definition.configPath, 'utf8'));
|
|
611
|
+
if (jsonExposesInternalGraphPeer(parsed))
|
|
612
|
+
return false;
|
|
585
613
|
if (definition.id === 'opencode') {
|
|
586
614
|
const server = parsed?.mcp?.['nexus-prime'];
|
|
587
615
|
return Boolean(server && isStableNexusMcpServerConfig(server, 'environment'));
|
|
@@ -813,6 +841,7 @@ program
|
|
|
813
841
|
.option('--standalone', 'Skip daemon detection, run in-process (debug mode)')
|
|
814
842
|
.action(async (options) => {
|
|
815
843
|
// stdio transport requires strict JSON-RPC on stdout — no console.log here.
|
|
844
|
+
primeMcpStdioInput();
|
|
816
845
|
const workspaceContext = resolveWorkspaceContext({ workspaceRoot: getWorkspaceRoot() });
|
|
817
846
|
const caller = detectCallerIDE();
|
|
818
847
|
process.env.NEXUS_DAEMON_AUTOSPAWN ??= '0';
|
|
@@ -955,6 +984,7 @@ program
|
|
|
955
984
|
throw new Error('MCP adapter did not become ready before the startup deadline.');
|
|
956
985
|
}
|
|
957
986
|
await startup;
|
|
987
|
+
flushPrimedMcpStdioInput();
|
|
958
988
|
console.error('Nexus Prime MCP Server running on stdio (standalone)');
|
|
959
989
|
console.error('Memory persistence: active (~/.nexus-prime/memory.db)');
|
|
960
990
|
const shutdown = async (signal) => {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
<link rel="stylesheet" href="./styles/board.css">
|
|
12
12
|
<link rel="stylesheet" href="./styles/workforce.css">
|
|
13
13
|
<link rel="stylesheet" href="./styles/memory.css">
|
|
14
|
+
<link rel="stylesheet" href="./styles/context-log.css">
|
|
14
15
|
<link rel="stylesheet" href="./styles/governance.css">
|
|
15
16
|
<link rel="stylesheet" href="./styles/trust.css">
|
|
16
17
|
<link rel="stylesheet" href="./styles/animation.css">
|
|
@@ -55,6 +56,7 @@ if(location.protocol==='file:'){
|
|
|
55
56
|
<a class="nav-item" data-nav="runtime" href="#runtime">⚡ Runtime</a>
|
|
56
57
|
<a class="nav-item" data-nav="workforce" href="#workforce">Workforce</a>
|
|
57
58
|
<a class="nav-item" data-nav="memory" href="#memory">Memory</a>
|
|
59
|
+
<a class="nav-item" data-nav="context-log" href="#context-log">Context Log</a>
|
|
58
60
|
<a class="nav-item" data-nav="repo" href="#repo">Repo</a>
|
|
59
61
|
<a class="nav-item" data-nav="knowledge" href="#knowledge">Knowledge</a>
|
|
60
62
|
<a class="nav-item" data-nav="governance" href="#governance">Governance</a>
|
|
@@ -214,12 +216,18 @@ if(location.protocol==='file:'){
|
|
|
214
216
|
<input id="mem-search" type="text" placeholder="Search memories…" aria-label="Search memories">
|
|
215
217
|
<button class="btn btn-sm" id="mem-list-browse-btn">Browse</button>
|
|
216
218
|
</div>
|
|
219
|
+
<div id="memory-selection-panel" class="memory-selection-panel"></div>
|
|
217
220
|
<div id="mem-list"></div>
|
|
218
221
|
</div>
|
|
219
222
|
<div class="card"><div id="mem-stats"></div></div>
|
|
220
223
|
</div>
|
|
221
224
|
</section>
|
|
222
225
|
|
|
226
|
+
<!-- CONTEXT LOG ────────────────────────────────────────────── -->
|
|
227
|
+
<section class="view-panel" data-view="context-log" aria-label="Context log">
|
|
228
|
+
<div id="context-log-view"></div>
|
|
229
|
+
</section>
|
|
230
|
+
|
|
223
231
|
<!-- REPO ───────────────────────────────────────────────────── -->
|
|
224
232
|
<section class="view-panel" data-view="repo" aria-label="Repo graph">
|
|
225
233
|
<div class="shd">Repo knowledge graph</div>
|
|
@@ -15,6 +15,7 @@ import { init as initCmdBar } from './widgets/command-bar.js';
|
|
|
15
15
|
import * as Board from './views/board.js';
|
|
16
16
|
import * as Workforce from './views/workforce.js';
|
|
17
17
|
import * as Memory from './views/memory.js';
|
|
18
|
+
import * as ContextLog from './views/context-log.js';
|
|
18
19
|
import * as Knowledge from './views/knowledge.js';
|
|
19
20
|
import * as Repo from './views/repo.js';
|
|
20
21
|
import * as Governance from './views/governance.js';
|
|
@@ -53,6 +54,7 @@ navRegister('board', Board.load);
|
|
|
53
54
|
navRegister('runtime', Runtime.load);
|
|
54
55
|
navRegister('workforce', Workforce.load);
|
|
55
56
|
navRegister('memory', Memory.load);
|
|
57
|
+
navRegister('context-log', ContextLog.load);
|
|
56
58
|
navRegister('repo', Repo.load);
|
|
57
59
|
navRegister('knowledge', Knowledge.load);
|
|
58
60
|
navRegister('governance', Governance.load);
|
|
@@ -113,10 +115,14 @@ setOnEvent(evt => {
|
|
|
113
115
|
// here we just refresh the surface payload so kanban + KPIs reflect the new run.
|
|
114
116
|
if (String(evt.type||'').startsWith('orchestration.')) {
|
|
115
117
|
bustCache('/api/dashboard/surface/operate');
|
|
118
|
+
bustCache('/api/runs');
|
|
116
119
|
if (tab === 'board') {
|
|
117
120
|
Board.handleOrchestrationEvent?.(evt);
|
|
118
121
|
Board.render();
|
|
119
122
|
}
|
|
123
|
+
if (tab === 'context-log') {
|
|
124
|
+
ContextLog.load();
|
|
125
|
+
}
|
|
120
126
|
}
|
|
121
127
|
// Workspace root promoted (e.g. from bootstrap hint) — refresh header immediately.
|
|
122
128
|
if (String(evt.type||'') === 'workspace.changed') {
|
|
@@ -300,6 +306,7 @@ function _reloadTab(tab) {
|
|
|
300
306
|
if (tab === 'board') Board.load();
|
|
301
307
|
if (tab === 'workforce') Workforce.load();
|
|
302
308
|
if (tab === 'memory') Memory.load();
|
|
309
|
+
if (tab === 'context-log') ContextLog.load();
|
|
303
310
|
if (tab === 'knowledge') Knowledge.load();
|
|
304
311
|
}
|
|
305
312
|
|
|
@@ -65,6 +65,7 @@ export const S = {
|
|
|
65
65
|
memQuery: '',
|
|
66
66
|
graphSim: null,
|
|
67
67
|
memLane: 'all',
|
|
68
|
+
selectedMemoryIds: [],
|
|
68
69
|
memorySurface: null,
|
|
69
70
|
topology: null,
|
|
70
71
|
|
|
@@ -81,6 +82,10 @@ export const S = {
|
|
|
81
82
|
// Orchestration pipeline (live decomposition + completion from SSE)
|
|
82
83
|
lastDecomposition: null,
|
|
83
84
|
lastCompletion: null,
|
|
85
|
+
lastDecisionSpine: null,
|
|
86
|
+
contextLogRuns: [],
|
|
87
|
+
contextLogSelectedRunId: null,
|
|
88
|
+
contextLogSpine: null,
|
|
84
89
|
|
|
85
90
|
// Neural Stream HUD ring buffer (latest SSE events, restored from v3.8.0)
|
|
86
91
|
neuralStream: [],
|
|
@@ -30,9 +30,11 @@
|
|
|
30
30
|
.kb-body { display: flex; flex-direction: column; gap: 6px; min-height: 100px; }
|
|
31
31
|
.kb-empty {
|
|
32
32
|
height: 80px; border: 1px dashed var(--border); border-radius: var(--radius);
|
|
33
|
-
display: flex; align-items: center; justify-content: center;
|
|
33
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
34
|
+
gap: 4px; padding: 0 14px; text-align: center;
|
|
34
35
|
}
|
|
35
|
-
.kb-empty-
|
|
36
|
+
.kb-empty-title { font-family: var(--font-mono); font-size: 0.78rem; color: var(--text-dim); }
|
|
37
|
+
.kb-empty-sub { font-size: 0.72rem; line-height: 1.35; color: var(--text-dim); opacity: 0.75; }
|
|
36
38
|
.kcard {
|
|
37
39
|
padding: 9px 11px; background: var(--bg-elevated); border: 1px solid var(--border);
|
|
38
40
|
border-radius: var(--radius); cursor: pointer;
|
|
@@ -168,6 +170,165 @@
|
|
|
168
170
|
.ledger-n { font-family: var(--font-mono); font-size: 0.78rem; color: var(--accent); flex-shrink: 0; min-width: 18px; }
|
|
169
171
|
.ledger-txt { color: var(--text-muted); }
|
|
170
172
|
|
|
173
|
+
/* ── Decision Spine ── */
|
|
174
|
+
.decision-spine-prompt {
|
|
175
|
+
padding: 9px 11px;
|
|
176
|
+
margin-bottom: 10px;
|
|
177
|
+
border: 1px solid var(--border);
|
|
178
|
+
border-radius: var(--radius);
|
|
179
|
+
background: var(--bg-panel);
|
|
180
|
+
color: var(--text-main);
|
|
181
|
+
font-size: 0.78rem;
|
|
182
|
+
line-height: 1.45;
|
|
183
|
+
}
|
|
184
|
+
.decision-spine-grid {
|
|
185
|
+
display: grid;
|
|
186
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
187
|
+
gap: 8px;
|
|
188
|
+
margin-bottom: 10px;
|
|
189
|
+
}
|
|
190
|
+
.decision-spine-card {
|
|
191
|
+
min-width: 0;
|
|
192
|
+
padding: 8px 10px;
|
|
193
|
+
border: 1px solid var(--border);
|
|
194
|
+
border-radius: var(--radius);
|
|
195
|
+
background: var(--bg-elevated);
|
|
196
|
+
}
|
|
197
|
+
.decision-spine-k {
|
|
198
|
+
font-family: var(--font-mono);
|
|
199
|
+
font-size: 0.68rem;
|
|
200
|
+
letter-spacing: 0.08em;
|
|
201
|
+
text-transform: uppercase;
|
|
202
|
+
color: var(--text-dim);
|
|
203
|
+
margin-bottom: 4px;
|
|
204
|
+
}
|
|
205
|
+
.decision-spine-v {
|
|
206
|
+
font-family: var(--font-mono);
|
|
207
|
+
font-size: 0.9rem;
|
|
208
|
+
color: var(--accent);
|
|
209
|
+
white-space: nowrap;
|
|
210
|
+
overflow: hidden;
|
|
211
|
+
text-overflow: ellipsis;
|
|
212
|
+
}
|
|
213
|
+
.decision-spine-sub {
|
|
214
|
+
margin-top: 3px;
|
|
215
|
+
color: var(--text-dim);
|
|
216
|
+
font-size: 0.72rem;
|
|
217
|
+
white-space: nowrap;
|
|
218
|
+
overflow: hidden;
|
|
219
|
+
text-overflow: ellipsis;
|
|
220
|
+
}
|
|
221
|
+
.decision-spine-block {
|
|
222
|
+
display: grid;
|
|
223
|
+
grid-template-columns: 68px 1fr;
|
|
224
|
+
gap: 8px;
|
|
225
|
+
align-items: start;
|
|
226
|
+
padding: 6px 0;
|
|
227
|
+
border-top: 1px solid var(--border);
|
|
228
|
+
font-size: 0.78rem;
|
|
229
|
+
}
|
|
230
|
+
.decision-spine-block > span {
|
|
231
|
+
font-family: var(--font-mono);
|
|
232
|
+
color: var(--text-dim);
|
|
233
|
+
text-transform: uppercase;
|
|
234
|
+
letter-spacing: 0.06em;
|
|
235
|
+
font-size: 0.68rem;
|
|
236
|
+
padding-top: 2px;
|
|
237
|
+
}
|
|
238
|
+
.decision-spine-list {
|
|
239
|
+
display: flex;
|
|
240
|
+
flex-wrap: wrap;
|
|
241
|
+
gap: 4px;
|
|
242
|
+
min-width: 0;
|
|
243
|
+
}
|
|
244
|
+
.decision-spine-latest {
|
|
245
|
+
margin-top: 10px;
|
|
246
|
+
padding: 7px 9px;
|
|
247
|
+
border-left: 2px solid var(--accent);
|
|
248
|
+
background: var(--bg-panel);
|
|
249
|
+
color: var(--text-muted);
|
|
250
|
+
font-size: 0.78rem;
|
|
251
|
+
line-height: 1.4;
|
|
252
|
+
}
|
|
253
|
+
.decision-spine-full-link {
|
|
254
|
+
float: right;
|
|
255
|
+
color: var(--accent);
|
|
256
|
+
font-family: var(--font-mono);
|
|
257
|
+
font-size: 0.68rem;
|
|
258
|
+
text-decoration: none;
|
|
259
|
+
}
|
|
260
|
+
.decision-spine-full-link:hover { text-decoration: underline; }
|
|
261
|
+
.decision-spine-mini {
|
|
262
|
+
margin-top: 8px;
|
|
263
|
+
padding: 8px 12px;
|
|
264
|
+
border-left: 3px solid var(--secondary);
|
|
265
|
+
background: var(--bg-panel);
|
|
266
|
+
}
|
|
267
|
+
.decision-spine-mini-head {
|
|
268
|
+
font-size: 0.78rem;
|
|
269
|
+
font-weight: 600;
|
|
270
|
+
margin-bottom: 5px;
|
|
271
|
+
}
|
|
272
|
+
.decision-spine-mini-row,
|
|
273
|
+
.decision-spine-mini-lists {
|
|
274
|
+
display: flex;
|
|
275
|
+
flex-wrap: wrap;
|
|
276
|
+
gap: 6px;
|
|
277
|
+
color: var(--text-dim);
|
|
278
|
+
font-family: var(--font-mono);
|
|
279
|
+
font-size: 0.72rem;
|
|
280
|
+
}
|
|
281
|
+
.decision-spine-mini-lists { margin-top: 6px; }
|
|
282
|
+
.decision-spine-browser {
|
|
283
|
+
display: grid;
|
|
284
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
285
|
+
gap: 10px;
|
|
286
|
+
margin-top: 10px;
|
|
287
|
+
padding-top: 10px;
|
|
288
|
+
border-top: 1px solid var(--border);
|
|
289
|
+
}
|
|
290
|
+
.decision-spine-browser-col {
|
|
291
|
+
min-width: 0;
|
|
292
|
+
}
|
|
293
|
+
.decision-spine-browser-title {
|
|
294
|
+
font-family: var(--font-mono);
|
|
295
|
+
font-size: 0.68rem;
|
|
296
|
+
letter-spacing: 0.06em;
|
|
297
|
+
text-transform: uppercase;
|
|
298
|
+
color: var(--text-dim);
|
|
299
|
+
margin-bottom: 6px;
|
|
300
|
+
}
|
|
301
|
+
.decision-spine-log-row {
|
|
302
|
+
min-width: 0;
|
|
303
|
+
padding: 7px 8px;
|
|
304
|
+
border: 1px solid var(--border);
|
|
305
|
+
border-radius: var(--radius);
|
|
306
|
+
background: var(--bg-panel);
|
|
307
|
+
margin-bottom: 6px;
|
|
308
|
+
}
|
|
309
|
+
.decision-spine-log-head {
|
|
310
|
+
display: flex;
|
|
311
|
+
align-items: center;
|
|
312
|
+
justify-content: space-between;
|
|
313
|
+
gap: 8px;
|
|
314
|
+
color: var(--accent);
|
|
315
|
+
font-family: var(--font-mono);
|
|
316
|
+
font-size: 0.72rem;
|
|
317
|
+
margin-bottom: 4px;
|
|
318
|
+
}
|
|
319
|
+
.decision-spine-log-reason {
|
|
320
|
+
color: var(--text-muted);
|
|
321
|
+
font-size: 0.76rem;
|
|
322
|
+
line-height: 1.35;
|
|
323
|
+
margin-bottom: 5px;
|
|
324
|
+
}
|
|
325
|
+
.decision-spine-log-ref,
|
|
326
|
+
.decision-spine-empty {
|
|
327
|
+
color: var(--text-dim);
|
|
328
|
+
font-size: 0.72rem;
|
|
329
|
+
line-height: 1.35;
|
|
330
|
+
}
|
|
331
|
+
|
|
171
332
|
/* ── Tasks Tab ── */
|
|
172
333
|
.task-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
|
|
173
334
|
.filter-grp { display: flex; gap: 2px; }
|