nexus-prime 7.9.14 → 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.
@@ -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;
@@ -160,6 +160,7 @@ export class MCPAdapter {
160
160
  currentTask = '';
161
161
  middlewarePipeline;
162
162
  scanCache = new Map();
163
+ sessionLifecycleFinalized = false;
163
164
  sciFiMatrixLog(title, metrics, intent) {
164
165
  const width = 76;
165
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`);
@@ -876,6 +877,9 @@ export class MCPAdapter {
876
877
  }
877
878
  else {
878
879
  this.telemetry.observeSuccessfulToolCall(toolName, args);
880
+ if (toolName === 'nexus_session_dna' && String(args.action ?? 'load') === 'generate') {
881
+ this.sessionLifecycleFinalized = true;
882
+ }
879
883
  }
880
884
  }
881
885
  // Surface meaningful tool calls on the SSE feed; persist only high-signal outcomes.
@@ -1159,22 +1163,70 @@ export class MCPAdapter {
1159
1163
  startWatchdog();
1160
1164
  console.error('[MCP Adapter] Connected — runtime tools active');
1161
1165
  }
1162
- async disconnect() {
1163
- // Auto-flush Session DNA on disconnect
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
+ }
1164
1184
  try {
1165
- const telemetry = this.telemetry.snapshot();
1166
1185
  this.sessionDNA.syncFromTelemetry({
1167
1186
  callCount: telemetry.callCount,
1168
1187
  memoriesStored: telemetry.memoriesStored,
1169
1188
  memoriesRecalled: telemetry.memoriesRecalled,
1170
1189
  tokensOptimized: telemetry.tokensOptimized,
1171
1190
  });
1172
- this.sessionDNA.flush();
1173
- console.error('[MCP Adapter] Session DNA flushed');
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}`);
1174
1223
  }
1175
1224
  catch (e) {
1176
- console.error('[MCP Adapter] Failed to flush Session DNA:', e);
1225
+ console.error('[MCP Adapter] Failed to finalize Session DNA:', e);
1177
1226
  }
1227
+ }
1228
+ async disconnect() {
1229
+ await this.finalizeSessionLifecycle('disconnect');
1178
1230
  await this.server.close();
1179
1231
  this.connected = false;
1180
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
@@ -197,9 +197,25 @@ function writeStandardMcpConfig(targetPath, workspaceRoot) {
197
197
  const existing = readJson(targetPath);
198
198
  existing.mcpServers = existing.mcpServers ?? {};
199
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
+ }
200
204
  ensureParentDir(targetPath);
201
205
  writeFileSync(targetPath, JSON.stringify(existing, null, 2));
202
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
+ }
203
219
  function writeOpencodeConfig(targetPath, workspaceRoot) {
204
220
  const existing = readJson(targetPath);
205
221
  const mcpConfig = buildStandardMcpServerConfig(workspaceRoot);
@@ -428,6 +444,15 @@ function buildInstructionFiles(clientId) {
428
444
  content: artifact.content,
429
445
  }));
430
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
+ }
431
456
  function getSetupDefinition(clientId) {
432
457
  const instructionFiles = buildInstructionFiles(clientId);
433
458
  if (clientId === 'codex') {
@@ -458,7 +483,7 @@ function getSetupDefinition(clientId) {
458
483
  return {
459
484
  id: clientId,
460
485
  label: 'Claude Desktop',
461
- configPath: join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
486
+ configPath: resolveClaudeDesktopConfigPath(),
462
487
  instructionFiles,
463
488
  };
464
489
  }
@@ -583,6 +608,8 @@ function hasExpectedConfig(definition) {
583
608
  }
584
609
  try {
585
610
  const parsed = JSON.parse(readFileSync(definition.configPath, 'utf8'));
611
+ if (jsonExposesInternalGraphPeer(parsed))
612
+ return false;
586
613
  if (definition.id === 'opencode') {
587
614
  const server = parsed?.mcp?.['nexus-prime'];
588
615
  return Boolean(server && isStableNexusMcpServerConfig(server, 'environment'));
@@ -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-txt { font-family: var(--font-mono); font-size: 0.78rem; color: var(--text-dim); }
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; }
@@ -0,0 +1,167 @@
1
+ .context-log-shell {
2
+ display: grid;
3
+ grid-template-columns: 260px minmax(0, 1fr);
4
+ gap: 14px;
5
+ align-items: start;
6
+ }
7
+
8
+ .context-log-rail,
9
+ .context-log-summary,
10
+ .context-log-row,
11
+ .context-log-decision {
12
+ background: var(--bg-elevated);
13
+ border: 1px solid var(--border);
14
+ border-radius: var(--radius);
15
+ }
16
+
17
+ .context-log-rail {
18
+ padding: 10px;
19
+ max-height: calc(100vh - 150px);
20
+ overflow-y: auto;
21
+ }
22
+
23
+ .context-log-rail-head {
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: space-between;
27
+ gap: 8px;
28
+ margin-bottom: 8px;
29
+ color: var(--text-muted);
30
+ font-family: var(--font-mono);
31
+ font-size: 0.78rem;
32
+ }
33
+
34
+ .context-log-run {
35
+ width: 100%;
36
+ display: flex;
37
+ flex-direction: column;
38
+ gap: 3px;
39
+ padding: 8px 9px;
40
+ margin-bottom: 6px;
41
+ border: 1px solid var(--border);
42
+ border-radius: var(--radius);
43
+ background: var(--bg-panel);
44
+ color: var(--text-muted);
45
+ text-align: left;
46
+ cursor: pointer;
47
+ }
48
+
49
+ .context-log-run.active {
50
+ border-color: rgba(0,255,136,0.38);
51
+ background: rgba(0,255,136,0.06);
52
+ color: var(--text-main);
53
+ }
54
+
55
+ .context-log-run span,
56
+ .context-log-row-head span {
57
+ font-family: var(--font-mono);
58
+ font-size: 0.78rem;
59
+ }
60
+
61
+ .context-log-run small {
62
+ color: var(--text-dim);
63
+ font-size: 0.72rem;
64
+ }
65
+
66
+ .context-log-main {
67
+ min-width: 0;
68
+ }
69
+
70
+ .context-log-summary {
71
+ display: grid;
72
+ grid-template-columns: repeat(5, minmax(90px, 1fr));
73
+ gap: 10px;
74
+ padding: 14px;
75
+ margin-bottom: 14px;
76
+ }
77
+
78
+ .context-log-kpi span,
79
+ .context-log-selection span {
80
+ display: block;
81
+ margin-bottom: 4px;
82
+ color: var(--text-dim);
83
+ font-family: var(--font-mono);
84
+ font-size: 0.68rem;
85
+ text-transform: uppercase;
86
+ letter-spacing: 0.05em;
87
+ }
88
+
89
+ .context-log-kpi strong {
90
+ color: var(--text-main);
91
+ font-size: 1rem;
92
+ }
93
+
94
+ .context-log-selection {
95
+ grid-column: 1 / -1;
96
+ display: grid;
97
+ grid-template-columns: repeat(3, minmax(0, 1fr));
98
+ gap: 10px;
99
+ }
100
+
101
+ .context-log-selection > div,
102
+ .context-log-refs {
103
+ display: flex;
104
+ flex-wrap: wrap;
105
+ gap: 4px;
106
+ min-width: 0;
107
+ }
108
+
109
+ .context-log-grid {
110
+ display: grid;
111
+ grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
112
+ gap: 14px;
113
+ }
114
+
115
+ .context-log-row,
116
+ .context-log-decision {
117
+ padding: 10px 12px;
118
+ margin-bottom: 8px;
119
+ }
120
+
121
+ .context-log-row-head {
122
+ display: grid;
123
+ grid-template-columns: auto auto minmax(0, 1fr);
124
+ gap: 8px;
125
+ align-items: center;
126
+ margin-bottom: 7px;
127
+ color: var(--accent);
128
+ }
129
+
130
+ .context-log-row-head time {
131
+ justify-self: end;
132
+ color: var(--text-dim);
133
+ font-family: var(--font-mono);
134
+ font-size: 0.7rem;
135
+ }
136
+
137
+ .context-log-row p,
138
+ .context-log-decision p {
139
+ margin: 0 0 8px;
140
+ color: var(--text-muted);
141
+ font-size: 0.78rem;
142
+ line-height: 1.45;
143
+ }
144
+
145
+ .context-log-decision strong {
146
+ display: block;
147
+ margin-bottom: 6px;
148
+ color: var(--text-main);
149
+ font-size: 0.82rem;
150
+ line-height: 1.35;
151
+ }
152
+
153
+ .context-log-empty {
154
+ color: var(--text-dim);
155
+ font-size: 0.72rem;
156
+ }
157
+
158
+ @media (max-width: 980px) {
159
+ .context-log-shell,
160
+ .context-log-grid,
161
+ .context-log-selection {
162
+ grid-template-columns: 1fr;
163
+ }
164
+ .context-log-summary {
165
+ grid-template-columns: repeat(2, minmax(0, 1fr));
166
+ }
167
+ }