nexus-prime 7.9.21 β 7.9.22
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/handlers/memory.js +44 -5
- package/dist/dashboard/app/styles/runtime.css +148 -0
- package/dist/dashboard/app/styles/workforce.css +28 -0
- package/dist/dashboard/app/views/runtime.js +138 -4
- package/dist/dashboard/app/views/workforce.js +11 -4
- package/dist/engines/event-bus.d.ts +4 -0
- package/package.json +1 -1
|
@@ -11,6 +11,35 @@ import { requireRuntime } from '../util/require-runtime.js';
|
|
|
11
11
|
import { nxlResult } from '../../../../nxl/index.js';
|
|
12
12
|
import { consolidateMemory } from '../../../../engines/memory-consolidator.js';
|
|
13
13
|
import { recordFirstMemory } from '../../../../engines/telemetry.js';
|
|
14
|
+
function memoryScopeForTags(tags) {
|
|
15
|
+
return tags.some((tag) => tag.startsWith('#project')) ? 'project' : 'session';
|
|
16
|
+
}
|
|
17
|
+
function memoryStatusSummary(hctx) {
|
|
18
|
+
try {
|
|
19
|
+
const stats = hctx.nexusRef.getMemoryStats?.();
|
|
20
|
+
const health = typeof hctx.nexusRef.getMemoryHealth === 'function'
|
|
21
|
+
? hctx.nexusRef.getMemoryHealth()
|
|
22
|
+
: null;
|
|
23
|
+
const healthText = health
|
|
24
|
+
? `${health.active} active, ${health.quarantined} quarantined, ${health.shared} shared`
|
|
25
|
+
: null;
|
|
26
|
+
const topTags = Array.isArray(stats?.topTags) && stats.topTags.length
|
|
27
|
+
? stats.topTags.slice(0, 4).join(', ')
|
|
28
|
+
: 'none yet';
|
|
29
|
+
const tierText = stats
|
|
30
|
+
? `${stats.prefrontal} working, ${stats.hippocampus} episodic, ${stats.cortex} semantic`
|
|
31
|
+
: null;
|
|
32
|
+
return [
|
|
33
|
+
'Memory status:',
|
|
34
|
+
tierText,
|
|
35
|
+
healthText,
|
|
36
|
+
`top tags ${topTags}`,
|
|
37
|
+
].filter(Boolean).join(' ');
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
14
43
|
export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
|
|
15
44
|
const runtimeError = requireRuntime(hctx);
|
|
16
45
|
if (runtimeError)
|
|
@@ -28,8 +57,10 @@ export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
|
|
|
28
57
|
void recordFirstMemory().catch(() => { });
|
|
29
58
|
hctx.sessionDNA.recordMemoryStore();
|
|
30
59
|
const nudge = hctx.telemetry.planningNudge('store', { priority });
|
|
60
|
+
const scope = memoryScopeForTags(tags);
|
|
61
|
+
const status = memoryStatusSummary(hctx);
|
|
31
62
|
if (ctx) {
|
|
32
|
-
ctx.meta.memoryScope =
|
|
63
|
+
ctx.meta.memoryScope = scope;
|
|
33
64
|
}
|
|
34
65
|
// Console ASCII UI
|
|
35
66
|
hctx.box('π§ CORTEX MEMORY STORED', [
|
|
@@ -41,6 +72,8 @@ export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
|
|
|
41
72
|
type: 'text',
|
|
42
73
|
text: [
|
|
43
74
|
'Memory stored. ID: ' + id,
|
|
75
|
+
`Memory link: ${scope} scope, priority ${priority.toFixed(2)}, ${tags.length} tag(s).`,
|
|
76
|
+
status,
|
|
44
77
|
nudge,
|
|
45
78
|
].filter(Boolean).join('\n\n'),
|
|
46
79
|
}],
|
|
@@ -52,13 +85,15 @@ export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
|
|
|
52
85
|
const fmt = String(request.params.arguments?.format ?? 'text');
|
|
53
86
|
const crossClient = Boolean(request.params.arguments?.cross_client ?? false);
|
|
54
87
|
const memories = await hctx.nexusRef.recallMemory(query, k, crossClient ? undefined : hctx.getToolProfile());
|
|
55
|
-
|
|
88
|
+
const preview = memories.slice(0, 3).map((m) => String(m ?? '').replace(/\s+/g, ' ').slice(0, 96));
|
|
89
|
+
nexusEventBus.emit('memory.recall', { query, count: memories.length, k, crossClient, format: fmt, preview });
|
|
56
90
|
hctx.telemetry.recordRecall(memories.length);
|
|
57
91
|
hctx.sessionDNA.recordMemoryRecall();
|
|
58
92
|
if (ctx) {
|
|
59
93
|
ctx.meta.memoryRecallCount = memories.length;
|
|
60
94
|
}
|
|
61
95
|
const nudge = hctx.telemetry.planningNudge('recall', { count: memories.length });
|
|
96
|
+
const status = memoryStatusSummary(hctx);
|
|
62
97
|
// Console ASCII UI
|
|
63
98
|
hctx.box('π CORTEX MEMORY RECALL', [
|
|
64
99
|
`Query: ${query.replace(/\n/g, ' ').substring(0, 57).padEnd(59, ' ')}`,
|
|
@@ -80,9 +115,13 @@ export async function handleMemoryGroup(toolName, hctx, request, args, ctx) {
|
|
|
80
115
|
return {
|
|
81
116
|
content: [{
|
|
82
117
|
type: 'text',
|
|
83
|
-
text:
|
|
84
|
-
|
|
85
|
-
|
|
118
|
+
text: [
|
|
119
|
+
memories.length > 0
|
|
120
|
+
? `Memory recall connected ${memories.length} item(s) for "${query}".\n\n${formatted}`
|
|
121
|
+
: `No memories found for "${query}". Fresh session or new topic.`,
|
|
122
|
+
status,
|
|
123
|
+
nudge,
|
|
124
|
+
].filter(Boolean).join('\n\n'),
|
|
86
125
|
}],
|
|
87
126
|
};
|
|
88
127
|
}
|
|
@@ -20,6 +20,25 @@
|
|
|
20
20
|
border-radius: 8px;
|
|
21
21
|
padding: 14px 18px;
|
|
22
22
|
min-width: 0;
|
|
23
|
+
text-align: left;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.rt-kpi-action {
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
color: inherit;
|
|
29
|
+
font: inherit;
|
|
30
|
+
transition: border-color 0.15s, background 0.15s, transform 0.15s;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.rt-kpi-action:hover,
|
|
34
|
+
.rt-kpi-action[aria-expanded="true"] {
|
|
35
|
+
border-color: var(--accent);
|
|
36
|
+
background: oklch(87% 0.19 152 / 7%);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.rt-kpi-action:focus-visible {
|
|
40
|
+
outline: 2px solid var(--accent);
|
|
41
|
+
outline-offset: 2px;
|
|
23
42
|
}
|
|
24
43
|
|
|
25
44
|
.rt-kpi-val {
|
|
@@ -234,6 +253,135 @@
|
|
|
234
253
|
min-height: 0;
|
|
235
254
|
}
|
|
236
255
|
|
|
256
|
+
/* ββ Token telemetry flyout βββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
|
257
|
+
.rt-token-flyout-slot {
|
|
258
|
+
position: relative;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.rt-token-flyout {
|
|
262
|
+
border: 1px solid var(--border);
|
|
263
|
+
border-radius: 8px;
|
|
264
|
+
background: var(--surface);
|
|
265
|
+
padding: 14px;
|
|
266
|
+
box-shadow: 0 18px 45px oklch(0% 0 0 / 35%);
|
|
267
|
+
animation: rt-ev-in 0.12s ease;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.rt-token-head {
|
|
271
|
+
display: flex;
|
|
272
|
+
align-items: flex-start;
|
|
273
|
+
justify-content: space-between;
|
|
274
|
+
gap: 12px;
|
|
275
|
+
margin-bottom: 12px;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.rt-token-title {
|
|
279
|
+
font: 700 14px ui-sans-serif, system-ui, sans-serif;
|
|
280
|
+
color: var(--text);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.rt-token-sub {
|
|
284
|
+
margin-top: 3px;
|
|
285
|
+
font-size: 12px;
|
|
286
|
+
color: var(--text-muted);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.rt-token-error { color: var(--bad, #ef4444); }
|
|
290
|
+
|
|
291
|
+
.rt-token-close {
|
|
292
|
+
border: 1px solid var(--border);
|
|
293
|
+
border-radius: 6px;
|
|
294
|
+
background: transparent;
|
|
295
|
+
color: var(--text-muted);
|
|
296
|
+
padding: 4px 9px;
|
|
297
|
+
cursor: pointer;
|
|
298
|
+
font-size: 12px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.rt-token-close:hover {
|
|
302
|
+
border-color: var(--accent);
|
|
303
|
+
color: var(--accent);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.rt-token-grid {
|
|
307
|
+
display: grid;
|
|
308
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
309
|
+
gap: 8px;
|
|
310
|
+
margin-bottom: 12px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.rt-token-stat {
|
|
314
|
+
border: 1px solid var(--border);
|
|
315
|
+
border-radius: 7px;
|
|
316
|
+
padding: 10px;
|
|
317
|
+
background: var(--bg-panel, transparent);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.rt-token-stat span,
|
|
321
|
+
.rt-token-row span {
|
|
322
|
+
color: var(--text-muted);
|
|
323
|
+
font-size: 11px;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.rt-token-stat strong {
|
|
327
|
+
display: block;
|
|
328
|
+
margin-top: 5px;
|
|
329
|
+
font: 700 20px/1 var(--font-mono, ui-monospace, monospace);
|
|
330
|
+
color: var(--accent);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.rt-token-columns {
|
|
334
|
+
display: grid;
|
|
335
|
+
grid-template-columns: 1fr 1fr;
|
|
336
|
+
gap: 12px;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.rt-token-section-title {
|
|
340
|
+
color: var(--text-muted);
|
|
341
|
+
font: 700 11px var(--font-mono, ui-monospace, monospace);
|
|
342
|
+
letter-spacing: 0.06em;
|
|
343
|
+
text-transform: uppercase;
|
|
344
|
+
margin-bottom: 6px;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.rt-token-row {
|
|
348
|
+
display: grid;
|
|
349
|
+
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
350
|
+
gap: 8px;
|
|
351
|
+
align-items: center;
|
|
352
|
+
border-top: 1px solid var(--border);
|
|
353
|
+
padding: 6px 0;
|
|
354
|
+
min-width: 0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.rt-token-row strong {
|
|
358
|
+
color: var(--text);
|
|
359
|
+
font: 700 12px var(--font-mono, ui-monospace, monospace);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.rt-token-row span:first-child {
|
|
363
|
+
overflow: hidden;
|
|
364
|
+
text-overflow: ellipsis;
|
|
365
|
+
white-space: nowrap;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.rt-token-muted {
|
|
369
|
+
color: var(--text-muted);
|
|
370
|
+
font-size: 12px;
|
|
371
|
+
padding: 8px 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
@media (max-width: 900px) {
|
|
375
|
+
.runtime-kpis,
|
|
376
|
+
.rt-token-grid,
|
|
377
|
+
.rt-token-columns {
|
|
378
|
+
grid-template-columns: 1fr;
|
|
379
|
+
}
|
|
380
|
+
.runtime-kpis {
|
|
381
|
+
display: grid;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
237
385
|
.rt-mcp-strip-inner {
|
|
238
386
|
display: flex;
|
|
239
387
|
flex-wrap: wrap;
|
|
@@ -101,6 +101,34 @@
|
|
|
101
101
|
}
|
|
102
102
|
.dtags { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
103
103
|
|
|
104
|
+
.dispatch-strip {
|
|
105
|
+
margin-top: 10px;
|
|
106
|
+
padding: 10px;
|
|
107
|
+
border: 1px solid var(--border);
|
|
108
|
+
border-radius: var(--radius);
|
|
109
|
+
background: var(--bg-panel);
|
|
110
|
+
}
|
|
111
|
+
.ds-stages { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin-bottom: 7px; }
|
|
112
|
+
.ds-stage {
|
|
113
|
+
font: 0.68rem var(--font-mono);
|
|
114
|
+
padding: 2px 6px;
|
|
115
|
+
border: 1px solid var(--border);
|
|
116
|
+
border-radius: 999px;
|
|
117
|
+
color: var(--text-dim);
|
|
118
|
+
}
|
|
119
|
+
.ds-stage.ds-done { color: var(--accent); border-color: rgba(0,255,136,0.28); }
|
|
120
|
+
.ds-stage.ds-active { color: var(--secondary); border-color: rgba(0,212,255,0.35); }
|
|
121
|
+
.ds-sep { color: var(--text-dim); font-size: 0.68rem; }
|
|
122
|
+
.ds-stdout, .ds-summary {
|
|
123
|
+
font-size: 0.74rem;
|
|
124
|
+
color: var(--text-muted);
|
|
125
|
+
line-height: 1.45;
|
|
126
|
+
margin-top: 5px;
|
|
127
|
+
}
|
|
128
|
+
.ds-meta { display: flex; flex-wrap: wrap; gap: 8px; color: var(--text-dim); font: 0.68rem var(--font-mono); }
|
|
129
|
+
.ds-error { margin-top: 6px; color: var(--warning); font-size: 0.74rem; }
|
|
130
|
+
.ds-stop-btn { margin-top: 8px; }
|
|
131
|
+
|
|
104
132
|
/* ββ Unified workforce kanban ββ */
|
|
105
133
|
.workforce-kanban { padding: 0; background: transparent; border: none !important; }
|
|
106
134
|
.kanban-meta { font-size: 0.75rem; color: var(--text-dim); margin-bottom: 8px; padding: 0 4px; }
|
|
@@ -30,6 +30,10 @@ let _settledToolTimes = new Map();
|
|
|
30
30
|
let _pulseTimer = null;
|
|
31
31
|
// toast queue
|
|
32
32
|
let _toastTimer = null;
|
|
33
|
+
let _tokenFlyoutOpen = false;
|
|
34
|
+
let _tokenFlyoutLoading = false;
|
|
35
|
+
let _tokenFlyoutError = '';
|
|
36
|
+
let _tokenTelemetry = null;
|
|
33
37
|
|
|
34
38
|
/* ββ Category metadata ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
|
35
39
|
const CATEGORY_META = {
|
|
@@ -65,6 +69,27 @@ function humanMs(ms) {
|
|
|
65
69
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
function fmtTokens(n) {
|
|
73
|
+
const v = Number(n ?? 0);
|
|
74
|
+
if (!Number.isFinite(v) || v <= 0) return '0';
|
|
75
|
+
if (v >= 1_000_000) return `${(v / 1_000_000).toFixed(1)}M`;
|
|
76
|
+
if (v >= 10_000) return `${Math.round(v / 1000)}k`;
|
|
77
|
+
if (v >= 1000) return `${(v / 1000).toFixed(1)}k`;
|
|
78
|
+
return Math.round(v).toLocaleString();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function fmtPct(n) {
|
|
82
|
+
const v = Number(n ?? 0);
|
|
83
|
+
if (!Number.isFinite(v)) return '0%';
|
|
84
|
+
return `${Math.round(v)}%`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function fmtTime(ts) {
|
|
88
|
+
const n = Number(ts ?? 0);
|
|
89
|
+
if (!Number.isFinite(n) || n <= 0) return 'recent';
|
|
90
|
+
return new Date(n).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
91
|
+
}
|
|
92
|
+
|
|
68
93
|
function toolNameFromPayload(payload) {
|
|
69
94
|
return String(payload.toolName ?? payload.tool ?? payload.name ?? '').trim();
|
|
70
95
|
}
|
|
@@ -159,10 +184,10 @@ function mount() {
|
|
|
159
184
|
<div class="rt-kpi-val" id="rt-toolcalls">0</div>
|
|
160
185
|
<div class="rt-kpi-lbl">Tool Calls</div>
|
|
161
186
|
</div>
|
|
162
|
-
<
|
|
187
|
+
<button class="rt-kpi rt-kpi-action" id="rt-kpi-saved" type="button" aria-expanded="false" aria-controls="rt-token-flyout" title="Open token telemetry">
|
|
163
188
|
<div class="rt-kpi-val" id="rt-tokens-saved">0</div>
|
|
164
189
|
<div class="rt-kpi-lbl">Tokens Saved</div>
|
|
165
|
-
</
|
|
190
|
+
</button>
|
|
166
191
|
<div class="rt-kpi">
|
|
167
192
|
<div class="rt-kpi-val" id="rt-active-count">0</div>
|
|
168
193
|
<div class="rt-kpi-lbl">Active Now</div>
|
|
@@ -170,6 +195,7 @@ function mount() {
|
|
|
170
195
|
</div>
|
|
171
196
|
|
|
172
197
|
<div class="rt-mcp-strip" id="rt-mcp-strip"></div>
|
|
198
|
+
<div class="rt-token-flyout-slot" id="rt-token-flyout-slot"></div>
|
|
173
199
|
|
|
174
200
|
<div class="rt-filter-bar">
|
|
175
201
|
<button class="rt-filter-btn active" data-filter="all">All</button>
|
|
@@ -212,6 +238,14 @@ function mount() {
|
|
|
212
238
|
renderAll();
|
|
213
239
|
});
|
|
214
240
|
|
|
241
|
+
$('rt-kpi-saved')?.addEventListener('click', () => {
|
|
242
|
+
_tokenFlyoutOpen = !_tokenFlyoutOpen;
|
|
243
|
+
renderTokenFlyout();
|
|
244
|
+
if (_tokenFlyoutOpen && !_tokenTelemetry && !_tokenFlyoutLoading) {
|
|
245
|
+
void loadTokenTelemetry();
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
215
249
|
_mounted = true;
|
|
216
250
|
renderAll();
|
|
217
251
|
}
|
|
@@ -220,6 +254,7 @@ function mount() {
|
|
|
220
254
|
function renderAll() {
|
|
221
255
|
expireStaleActiveTools();
|
|
222
256
|
renderKPIs();
|
|
257
|
+
renderTokenFlyout();
|
|
223
258
|
renderMcpStrip();
|
|
224
259
|
renderFeed();
|
|
225
260
|
}
|
|
@@ -233,6 +268,96 @@ function renderKPIs() {
|
|
|
233
268
|
? (_totalTokensSaved >= 1000 ? `${(_totalTokensSaved / 1000).toFixed(1)}k` : String(_totalTokensSaved))
|
|
234
269
|
: '0';
|
|
235
270
|
if (activeEl) activeEl.textContent = String(_activeTools.size);
|
|
271
|
+
const tokenBtn = $('rt-kpi-saved');
|
|
272
|
+
if (tokenBtn) tokenBtn.setAttribute('aria-expanded', _tokenFlyoutOpen ? 'true' : 'false');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function loadTokenTelemetry() {
|
|
276
|
+
_tokenFlyoutLoading = true;
|
|
277
|
+
_tokenFlyoutError = '';
|
|
278
|
+
renderTokenFlyout();
|
|
279
|
+
try {
|
|
280
|
+
const [summary, lifetimeRaw, bySource, timeline] = await Promise.all([
|
|
281
|
+
api('/api/tokens/summary', 0),
|
|
282
|
+
api('/api/tokens/lifetime', 0),
|
|
283
|
+
api('/api/tokens/by-source', 0),
|
|
284
|
+
api('/api/tokens/timeline?limit=8', 0),
|
|
285
|
+
]);
|
|
286
|
+
_tokenTelemetry = {
|
|
287
|
+
summary: summary ?? {},
|
|
288
|
+
lifetime: lifetimeRaw?.data ?? lifetimeRaw ?? {},
|
|
289
|
+
bySource: bySource ?? {},
|
|
290
|
+
timeline: Array.isArray(timeline) ? timeline : [],
|
|
291
|
+
};
|
|
292
|
+
} catch (err) {
|
|
293
|
+
_tokenFlyoutError = err?.message || String(err);
|
|
294
|
+
} finally {
|
|
295
|
+
_tokenFlyoutLoading = false;
|
|
296
|
+
renderTokenFlyout();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function renderTokenFlyout() {
|
|
301
|
+
const slot = $('rt-token-flyout-slot');
|
|
302
|
+
if (!slot) return;
|
|
303
|
+
const btn = $('rt-kpi-saved');
|
|
304
|
+
if (btn) btn.setAttribute('aria-expanded', _tokenFlyoutOpen ? 'true' : 'false');
|
|
305
|
+
if (!_tokenFlyoutOpen) {
|
|
306
|
+
slot.innerHTML = '';
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (_tokenFlyoutLoading && !_tokenTelemetry) {
|
|
311
|
+
slot.innerHTML = `<div class="rt-token-flyout" id="rt-token-flyout">
|
|
312
|
+
<div class="rt-token-head"><div><div class="rt-token-title">Token telemetry</div><div class="rt-token-sub">Loading current runtime ledger...</div></div><button class="rt-token-close" id="rt-token-close" type="button">Close</button></div>
|
|
313
|
+
</div>`;
|
|
314
|
+
} else if (_tokenFlyoutError) {
|
|
315
|
+
slot.innerHTML = `<div class="rt-token-flyout" id="rt-token-flyout">
|
|
316
|
+
<div class="rt-token-head"><div><div class="rt-token-title">Token telemetry</div><div class="rt-token-sub rt-token-error">${esc(_tokenFlyoutError)}</div></div><button class="rt-token-close" id="rt-token-close" type="button">Close</button></div>
|
|
317
|
+
</div>`;
|
|
318
|
+
} else {
|
|
319
|
+
const data = _tokenTelemetry ?? {};
|
|
320
|
+
const summary = data.summary ?? {};
|
|
321
|
+
const lifetime = data.lifetime ?? {};
|
|
322
|
+
const bySource = data.bySource ?? {};
|
|
323
|
+
const timeline = Array.isArray(data.timeline) ? data.timeline : [];
|
|
324
|
+
const sourceRows = Object.entries(bySource).slice(0, 5).map(([source, value]) => {
|
|
325
|
+
const item = typeof value === 'object' && value ? value : { savedTokens: Number(value ?? 0) };
|
|
326
|
+
const saved = item.savedTokens ?? item.tokensSaved ?? item.saved ?? 0;
|
|
327
|
+
const gross = item.grossInputTokens ?? item.tokensOptimized ?? item.gross ?? 0;
|
|
328
|
+
return `<div class="rt-token-row"><span>${esc(source)}</span><strong>${fmtTokens(saved)}</strong><span>${fmtTokens(gross)} gross</span></div>`;
|
|
329
|
+
}).join('') || '<div class="rt-token-muted">No source ledger yet.</div>';
|
|
330
|
+
const timelineRows = timeline.slice(0, 6).map((item) => {
|
|
331
|
+
const saved = item.savedTokens ?? item.tokensSaved ?? item.saved ?? 0;
|
|
332
|
+
const run = item.runId ?? item.sessionId ?? item.source ?? 'runtime';
|
|
333
|
+
return `<div class="rt-token-row"><span>${esc(String(run).slice(0, 18))}</span><strong>${fmtTokens(saved)}</strong><span>${fmtTime(item.timestamp ?? item.ts ?? item.time)}</span></div>`;
|
|
334
|
+
}).join('') || '<div class="rt-token-muted">No recent token events yet.</div>';
|
|
335
|
+
slot.innerHTML = `<div class="rt-token-flyout" id="rt-token-flyout">
|
|
336
|
+
<div class="rt-token-head">
|
|
337
|
+
<div>
|
|
338
|
+
<div class="rt-token-title">Token telemetry</div>
|
|
339
|
+
<div class="rt-token-sub">Runtime ledger, lifetime savings, and live SSE savings for this tab.</div>
|
|
340
|
+
</div>
|
|
341
|
+
<button class="rt-token-close" id="rt-token-close" type="button">Close</button>
|
|
342
|
+
</div>
|
|
343
|
+
<div class="rt-token-grid">
|
|
344
|
+
<div class="rt-token-stat"><span>Recent saved</span><strong>${fmtTokens(summary.savedTokens ?? summary.saved)}</strong></div>
|
|
345
|
+
<div class="rt-token-stat"><span>Recent compression</span><strong>${fmtPct(summary.compressionPct)}</strong></div>
|
|
346
|
+
<div class="rt-token-stat"><span>Lifetime saved</span><strong>${fmtTokens(lifetime.savedTokens ?? lifetime.totalSaved)}</strong></div>
|
|
347
|
+
<div class="rt-token-stat"><span>Live tab saved</span><strong>${fmtTokens(_totalTokensSaved)}</strong></div>
|
|
348
|
+
</div>
|
|
349
|
+
<div class="rt-token-columns">
|
|
350
|
+
<div><div class="rt-token-section-title">By source</div>${sourceRows}</div>
|
|
351
|
+
<div><div class="rt-token-section-title">Recent runs</div>${timelineRows}</div>
|
|
352
|
+
</div>
|
|
353
|
+
</div>`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
$('rt-token-close')?.addEventListener('click', () => {
|
|
357
|
+
_tokenFlyoutOpen = false;
|
|
358
|
+
renderTokenFlyout();
|
|
359
|
+
renderKPIs();
|
|
360
|
+
});
|
|
236
361
|
}
|
|
237
362
|
|
|
238
363
|
function renderMcpStrip() {
|
|
@@ -421,11 +546,20 @@ export function ingestEvent(evt) {
|
|
|
421
546
|
break;
|
|
422
547
|
case 'memory.store':
|
|
423
548
|
label = 'memory Β· store';
|
|
424
|
-
detail =
|
|
549
|
+
detail = [
|
|
550
|
+
payload.id ? `id ${String(payload.id).slice(0, 12)}` : null,
|
|
551
|
+
payload.tier ? `tier ${payload.tier}` : null,
|
|
552
|
+
payload.priority != null ? `priority ${Number(payload.priority).toFixed(2)}` : null,
|
|
553
|
+
Array.isArray(payload.tags) && payload.tags.length ? `tags ${payload.tags.slice(0, 3).join(', ')}` : null,
|
|
554
|
+
].filter(Boolean).join(' Β· ') || String(payload.key ?? payload.content ?? '').slice(0, 80);
|
|
425
555
|
break;
|
|
426
556
|
case 'memory.recall':
|
|
427
557
|
label = 'memory Β· recall';
|
|
428
|
-
detail =
|
|
558
|
+
detail = [
|
|
559
|
+
`${Number(payload.count ?? 0)} recalled`,
|
|
560
|
+
payload.crossClient ? 'cross-client' : null,
|
|
561
|
+
String(payload.query ?? '').slice(0, 60),
|
|
562
|
+
].filter(Boolean).join(' Β· ');
|
|
429
563
|
break;
|
|
430
564
|
case 'tokens.optimized': {
|
|
431
565
|
const src = payload.source ? ` Β· ${payload.source}` : '';
|
|
@@ -45,6 +45,7 @@ export function handleDispatchEvent(evt) {
|
|
|
45
45
|
const type = evt.type ?? '';
|
|
46
46
|
|
|
47
47
|
if (type === 'dispatch.started') {
|
|
48
|
+
if (oid) _dispatches.delete(`__warmup__:${oid}`);
|
|
48
49
|
run.status = 'spawning';
|
|
49
50
|
run.invoker = p.invokerId ?? '';
|
|
50
51
|
} else if (type === 'dispatch.event') {
|
|
@@ -117,18 +118,20 @@ function _buildDispatchStrip(run) {
|
|
|
117
118
|
}).join('<span class="ds-sep">β</span>');
|
|
118
119
|
|
|
119
120
|
const isFinal = ['complete','failed','cancelled'].includes(run.status);
|
|
121
|
+
const isPendingAdapter = run.pendingAdapter === true;
|
|
120
122
|
|
|
121
123
|
return `<div class="dispatch-strip" data-run-id="${esc(run.runId)}">
|
|
122
124
|
<div class="ds-stages">${stageHtml}</div>
|
|
123
125
|
${run.messages.length ? `<div class="ds-stdout">${esc(run.messages[run.messages.length - 1])}</div>` : ''}
|
|
124
126
|
<div class="ds-meta">
|
|
127
|
+
${run.invoker ? `<span>${esc(run.invoker)}</span>` : ''}
|
|
125
128
|
${run.tokens ? `<span>${run.tokens.toLocaleString()} tokens</span>` : ''}
|
|
126
129
|
${run.costUsd ? `<span>$${run.costUsd.toFixed(4)}</span>` : ''}
|
|
127
130
|
${run.filesChanged.length ? `<span>${run.filesChanged.length} file(s)</span>` : ''}
|
|
128
131
|
</div>
|
|
129
132
|
${run.summary ? `<div class="ds-summary">${esc(String(run.summary).slice(0, 160))}</div>` : ''}
|
|
130
133
|
${run.error ? `<div class="ds-error">${esc(run.error)}</div>` : ''}
|
|
131
|
-
${!isFinal ? `<button class="btn btn-sm ds-stop-btn" data-stop-run="${esc(run.runId)}">Stop</button>` : ''}
|
|
134
|
+
${!isFinal && !isPendingAdapter ? `<button class="btn btn-sm ds-stop-btn" data-stop-run="${esc(run.runId)}">Stop</button>` : ''}
|
|
132
135
|
</div>`;
|
|
133
136
|
}
|
|
134
137
|
|
|
@@ -560,14 +563,18 @@ function _showHireSheet(specialistId, name) {
|
|
|
560
563
|
stripDiv.setAttribute('data-dispatch-strip', '');
|
|
561
564
|
drawerBody.appendChild(stripDiv);
|
|
562
565
|
}
|
|
563
|
-
const
|
|
564
|
-
|
|
566
|
+
const warmupKey = `__warmup__:${operativeId}`;
|
|
567
|
+
const warmupRun = { runId: warmupKey, operativeId, status: 'queued', tokens: 0, costUsd: 0, messages: ['Adapter pending; waiting for dispatch.startedβ¦'], filesChanged: [], pendingAdapter: true };
|
|
568
|
+
_dispatches.set(warmupKey, warmupRun);
|
|
565
569
|
stripDiv.innerHTML = _buildDispatchStrip(warmupRun);
|
|
566
570
|
}
|
|
567
571
|
const fd = real.data?.firstDispatch;
|
|
568
|
-
_dispatches.delete('__warmup__');
|
|
569
572
|
if (fd?.runId) {
|
|
573
|
+
_dispatches.delete(`__warmup__:${operativeId}`);
|
|
570
574
|
_dispatches.set(fd.runId, { runId: fd.runId, operativeId, status: 'queued', tokens: 0, costUsd: 0, messages: [], filesChanged: [] });
|
|
575
|
+
} else if (fd?.queued) {
|
|
576
|
+
const pending = _dispatches.get(`__warmup__:${operativeId}`);
|
|
577
|
+
if (pending) pending.messages = ['First sortie queued; adapter start will stream here.'];
|
|
571
578
|
}
|
|
572
579
|
_refreshDrawerForOp(operativeId);
|
|
573
580
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-prime",
|
|
3
|
-
"version": "7.9.
|
|
3
|
+
"version": "7.9.22",
|
|
4
4
|
"description": "Local-first MCP control plane for coding agents with bootstrap-orchestrate execution, memory fabric, token budgeting, and worktree-backed swarms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|