nexus-prime 7.9.25 → 7.9.26
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/dashboard/app/styles/board.css +34 -0
- package/dist/dashboard/app/views/board.js +39 -9
- package/dist/engines/event-bus.d.ts +16 -0
- package/dist/engines/orchestrator.js +3 -1
- package/dist/install/claude-code-hooks.js +4 -0
- package/dist/phantom/runtime.d.ts +6 -0
- package/dist/phantom/runtime.js +16 -0
- package/package.json +1 -1
|
@@ -479,6 +479,38 @@
|
|
|
479
479
|
color: var(--text-main); margin-bottom: 4px;
|
|
480
480
|
}
|
|
481
481
|
.frh-sub { font-size: var(--text-sm); color: var(--text-dim); }
|
|
482
|
+
.frh-steps {
|
|
483
|
+
display: grid;
|
|
484
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
485
|
+
gap: 8px;
|
|
486
|
+
margin-bottom: 14px;
|
|
487
|
+
}
|
|
488
|
+
.frh-step {
|
|
489
|
+
display: grid;
|
|
490
|
+
grid-template-columns: auto 1fr;
|
|
491
|
+
grid-template-rows: auto auto;
|
|
492
|
+
gap: 1px 8px;
|
|
493
|
+
align-items: center;
|
|
494
|
+
padding: 9px 10px;
|
|
495
|
+
border: 1px solid color-mix(in oklch, var(--accent) 24%, var(--border));
|
|
496
|
+
border-radius: var(--radius);
|
|
497
|
+
background: color-mix(in oklch, var(--accent) 4%, var(--bg-panel));
|
|
498
|
+
min-width: 0;
|
|
499
|
+
}
|
|
500
|
+
.frh-step span {
|
|
501
|
+
grid-row: 1 / span 2;
|
|
502
|
+
display: inline-grid;
|
|
503
|
+
place-items: center;
|
|
504
|
+
width: 22px;
|
|
505
|
+
height: 22px;
|
|
506
|
+
border-radius: 50%;
|
|
507
|
+
background: color-mix(in oklch, var(--accent) 18%, transparent);
|
|
508
|
+
color: var(--accent);
|
|
509
|
+
font-family: var(--font-mono);
|
|
510
|
+
font-size: 0.72rem;
|
|
511
|
+
}
|
|
512
|
+
.frh-step strong { font-size: 0.78rem; color: var(--text-main); min-width: 0; }
|
|
513
|
+
.frh-step small { font-size: 0.72rem; color: var(--text-dim); min-width: 0; }
|
|
482
514
|
.frh-command {
|
|
483
515
|
display: grid;
|
|
484
516
|
grid-template-columns: minmax(220px, 1fr) auto auto;
|
|
@@ -492,6 +524,7 @@
|
|
|
492
524
|
transition: border-color 0.15s;
|
|
493
525
|
}
|
|
494
526
|
.frh-pick:hover { border-color: var(--accent); }
|
|
527
|
+
.frh-pick-loading { border-style: dashed; }
|
|
495
528
|
.frh-pick-name { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--text-main); }
|
|
496
529
|
.frh-pick-desc { font-size: 0.78rem; color: var(--text-dim); flex: 1; }
|
|
497
530
|
.frh-pick-cost { font-family: var(--font-mono); font-size: 0.75rem; color: var(--accent); margin-top: 2px; }
|
|
@@ -507,6 +540,7 @@
|
|
|
507
540
|
}
|
|
508
541
|
@media (max-width: 768px) {
|
|
509
542
|
#kanban-board { grid-template-columns: repeat(2, 1fr); }
|
|
543
|
+
.frh-steps,
|
|
510
544
|
.frh-command,
|
|
511
545
|
.frh-picks { grid-template-columns: 1fr; }
|
|
512
546
|
}
|
|
@@ -376,7 +376,6 @@ function renderFirstRunHero() {
|
|
|
376
376
|
if (hasOps || (alreadySeen && hasRuns)) return;
|
|
377
377
|
|
|
378
378
|
const specs = (S.curatedSpecialists || []).slice(0, 3);
|
|
379
|
-
if (!specs.length) return; // Still loading — will re-render when prefetch resolves
|
|
380
379
|
const readiness = getHireReadiness();
|
|
381
380
|
const noticesHtml = readiness.notes.length
|
|
382
381
|
? `<div class="frh-notices" style="display:flex;flex-direction:column;gap:8px;margin-bottom:12px">
|
|
@@ -389,23 +388,33 @@ function renderFirstRunHero() {
|
|
|
389
388
|
card.className = 'first-run-hero card';
|
|
390
389
|
card.innerHTML = `
|
|
391
390
|
<div class="frh-header">
|
|
392
|
-
<div class="frh-title">${hasRuns ? '
|
|
393
|
-
<div class="frh-sub">${hasRuns ? 'Hire a specialist
|
|
391
|
+
<div class="frh-title">${hasRuns ? 'Choose the next worker' : 'Start the first real run'}</div>
|
|
392
|
+
<div class="frh-sub">${hasRuns ? 'Hire a specialist or queue a goal. Nexus will show the route, budget, workers, and verification here.' : 'Queue one goal from the dashboard. Nexus will route it, show who gets hired, and write the run trail into Board and Context Log.'}</div>
|
|
394
393
|
</div>
|
|
395
394
|
${noticesHtml}
|
|
395
|
+
<div class="frh-steps" aria-label="Onboarding steps">
|
|
396
|
+
<div class="frh-step"><span>1</span><strong>Describe goal</strong><small>Run or hire from here.</small></div>
|
|
397
|
+
<div class="frh-step"><span>2</span><strong>Watch route</strong><small>Board shows workers and budget.</small></div>
|
|
398
|
+
<div class="frh-step"><span>3</span><strong>Verify proof</strong><small>Context Log keeps artifacts.</small></div>
|
|
399
|
+
</div>
|
|
396
400
|
<div class="frh-command">
|
|
397
401
|
<input id="frh-goal-input" class="form-input" type="text" placeholder="Inspect this repo and suggest the next fix" autocomplete="off">
|
|
398
|
-
<button class="btn btn-primary btn-sm" id="frh-run-btn">Run goal</button>
|
|
402
|
+
<button class="btn btn-primary btn-sm" id="frh-run-btn">Run first goal</button>
|
|
399
403
|
<button class="btn btn-sm" id="frh-context-btn">Open context</button>
|
|
400
404
|
</div>
|
|
401
405
|
<div class="frh-picks">
|
|
402
|
-
${specs.map(s => `
|
|
406
|
+
${specs.length ? specs.map(s => `
|
|
403
407
|
<div class="frh-pick" data-specid="${esc(s.specialistId)}" data-specname="${esc(s.name)}">
|
|
404
408
|
<div class="frh-pick-name">${esc(s.name)}</div>
|
|
405
409
|
<div class="frh-pick-desc">${esc((s.description||'').slice(0, 72))}${(s.description||'').length > 72 ? '…' : ''}</div>
|
|
406
410
|
<div class="frh-pick-cost">~$${esc(String(s.pricing?.typical ?? '?'))}/sortie</div>
|
|
407
411
|
<button class="btn btn-primary btn-sm frh-hire-btn" data-specid="${esc(s.specialistId)}" data-specname="${esc(s.name)}" ${readiness.unavailable ? 'disabled title="Synapse is not ready"' : ''}>Hire</button>
|
|
408
|
-
</div>`).join('')
|
|
412
|
+
</div>`).join('') : `
|
|
413
|
+
<div class="frh-pick frh-pick-loading">
|
|
414
|
+
<div class="frh-pick-name">Specialists loading</div>
|
|
415
|
+
<div class="frh-pick-desc">You can run a goal immediately. Hiring picks will appear when the catalog responds.</div>
|
|
416
|
+
<div class="frh-pick-cost">route first, hire second</div>
|
|
417
|
+
</div>`}
|
|
409
418
|
</div>
|
|
410
419
|
<div id="frh-status" style="display:none;margin-top:12px;font-size:var(--text-sm)"></div>
|
|
411
420
|
<button class="btn btn-ghost btn-sm frh-dismiss" style="margin-top:var(--space-3)">Dismiss</button>`;
|
|
@@ -424,6 +433,7 @@ function renderFirstRunHero() {
|
|
|
424
433
|
const result = await post('/api/orchestrate', { goal, source: 'dashboard-onboarding' });
|
|
425
434
|
if (result.ok) {
|
|
426
435
|
setFirstRunStatus('Run queued. Board and Context Log will update as Nexus writes artifacts.');
|
|
436
|
+
try { localStorage.setItem(FIRST_RUN_KEY, '1'); } catch { /* ignore */ }
|
|
427
437
|
bustCache('/api/runs?limit=12');
|
|
428
438
|
bustCache('/api/events');
|
|
429
439
|
setTimeout(load, 900);
|
|
@@ -431,7 +441,7 @@ function renderFirstRunHero() {
|
|
|
431
441
|
setFirstRunStatus(result.error || 'Run failed to queue.', 'bad');
|
|
432
442
|
if (button) {
|
|
433
443
|
button.disabled = false;
|
|
434
|
-
button.textContent = 'Run goal';
|
|
444
|
+
button.textContent = 'Run first goal';
|
|
435
445
|
}
|
|
436
446
|
}
|
|
437
447
|
});
|
|
@@ -508,6 +518,21 @@ function buildKanbanCols() {
|
|
|
508
518
|
if (sg) cols[sg].push({id:w.id||w.workerId||w.goal,goal:w.goal||w.task||w.approach||'(worker)',status:st,tokens:w.tokensUsed||w.budget,time:w.startedAt||w.createdAt,role:w.role});
|
|
509
519
|
}
|
|
510
520
|
}
|
|
521
|
+
const ghost = S.lastDecomposition?.autoGhostPass || S.lastCompletion?.autoGhostPass || op?.orchestration?.autoGhostPass || op?.autoGhostPass;
|
|
522
|
+
if (ghost && (ghost.applied || ghost.policy?.reason)) {
|
|
523
|
+
const risks = Array.isArray(ghost.riskAreas) ? ghost.riskAreas.length : 0;
|
|
524
|
+
const reason = ghost.policy?.reason ? ` · ${ghost.policy.reason}` : '';
|
|
525
|
+
cols.ghostpass.push({
|
|
526
|
+
id: 'auto-ghostpass',
|
|
527
|
+
goal: ghost.applied
|
|
528
|
+
? `Auto Ghost Pass: ${risks} risk area${risks === 1 ? '' : 's'}, ${ghost.workerApproaches || 0} approach${ghost.workerApproaches === 1 ? '' : 'es'}`
|
|
529
|
+
: `Auto Ghost Pass skipped${reason}`,
|
|
530
|
+
status: ghost.applied ? 'reviewing' : 'skipped',
|
|
531
|
+
tokens: ghost.estimatedTokens,
|
|
532
|
+
time: S.lastDecomposition?.ts || S.lastCompletion?.ts,
|
|
533
|
+
role: 'ghost-pass',
|
|
534
|
+
});
|
|
535
|
+
}
|
|
511
536
|
for (const r of (S.runs||[]).slice(0,8)) {
|
|
512
537
|
const runId = r.runId || r.id;
|
|
513
538
|
if (!runId) continue;
|
|
@@ -874,11 +899,16 @@ function renderOrchestrationPipeline() {
|
|
|
874
899
|
const more = arr.length > 6 ? ` <span style="color:var(--muted)">+${arr.length - 6}</span>` : '';
|
|
875
900
|
return `<div style="margin:4px 0"><span style="color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:0.5px">${esc(label)}</span> <span style="font-family:var(--font-mono);font-size:12px">${head}${more}</span></div>`;
|
|
876
901
|
};
|
|
902
|
+
const ghostChip = (ghost) => ghost
|
|
903
|
+
? chip('ghost-pass', ghost.applied
|
|
904
|
+
? `${ghost.riskAreas?.length ?? 0} risks · ${ghost.workerApproaches ?? 0} approaches`
|
|
905
|
+
: `skipped${ghost.policy?.reason ? ` · ${ghost.policy.reason}` : ''}`)
|
|
906
|
+
: '';
|
|
877
907
|
const decBlock = dec ? `
|
|
878
908
|
<div style="border-left:3px solid var(--accent);padding:8px 12px;margin-bottom:8px">
|
|
879
909
|
<div style="font-size:13px;font-weight:600;margin-bottom:4px">Decomposition · run ${esc((dec.runId || '').slice(-8))}</div>
|
|
880
910
|
<div style="font-size:12px;color:var(--muted);margin-bottom:6px">${esc(dec.goal || '')}</div>
|
|
881
|
-
<div>${chip('intent', dec.intent || 'auto')}${chip('crew', dec.crew || 'baseline')}${chip('workers', dec.workers ?? 0)}${chip('phases', dec.phases ?? 0)}</div>
|
|
911
|
+
<div>${chip('intent', dec.intent || 'auto')}${chip('crew', dec.crew || 'baseline')}${chip('workers', dec.workers ?? 0)}${chip('phases', dec.phases ?? 0)}${ghostChip(dec.autoGhostPass)}</div>
|
|
882
912
|
${chipList('specialists', dec.specialists)}
|
|
883
913
|
${chipList('skills', dec.skills)}
|
|
884
914
|
${chipList('files', dec.files)}
|
|
@@ -888,7 +918,7 @@ function renderOrchestrationPipeline() {
|
|
|
888
918
|
<div style="border-left:3px solid ${stateColor(cmpState)};padding:8px 12px">
|
|
889
919
|
<div style="font-size:13px;font-weight:600;margin-bottom:4px">Completion · run ${esc((cmp.runId || '').slice(-8))} · <span style="color:${stateColor(cmpState)}">${esc(cmpState || '')}</span></div>
|
|
890
920
|
<div style="font-size:12px;color:var(--muted);margin-bottom:6px">${esc(cmp.result || '')}</div>
|
|
891
|
-
<div>${chip('verified', `${cmp.verifiedWorkers ?? 0}/${cmp.totalWorkers ?? 0}`)}${chip('saved', `${fmtNum(cmp.savedTokens ?? 0)} t`)}${chip('compression', `${cmp.compressionPct ?? 0}%`)}${chip('duration', `${Math.round((cmp.durationMs ?? 0) / 100) / 10}s`)}</div>
|
|
921
|
+
<div>${chip('verified', `${cmp.verifiedWorkers ?? 0}/${cmp.totalWorkers ?? 0}`)}${chip('saved', `${fmtNum(cmp.savedTokens ?? 0)} t`)}${chip('compression', `${cmp.compressionPct ?? 0}%`)}${chip('duration', `${Math.round((cmp.durationMs ?? 0) / 100) / 10}s`)}${ghostChip(cmp.autoGhostPass)}</div>
|
|
892
922
|
</div>` : '';
|
|
893
923
|
const spineBlock = spine ? _buildDecisionSpineMiniHtml(spine) : '';
|
|
894
924
|
const headerNote = same ? '' : (dec && cmp ? '<div style="font-size:11px;color:var(--muted);margin-bottom:6px">Showing latest decomposition + most recent completion (different runs)</div>' : '');
|
|
@@ -93,6 +93,16 @@ export interface NexusEventPayloads {
|
|
|
93
93
|
files: string[];
|
|
94
94
|
workers: number;
|
|
95
95
|
phases: number;
|
|
96
|
+
autoGhostPass?: {
|
|
97
|
+
applied: boolean;
|
|
98
|
+
riskAreas: string[];
|
|
99
|
+
workerApproaches: number;
|
|
100
|
+
estimatedTokens: number;
|
|
101
|
+
policy?: {
|
|
102
|
+
enabled?: boolean;
|
|
103
|
+
reason?: string;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
96
106
|
};
|
|
97
107
|
'orchestration.completed': {
|
|
98
108
|
runId: string;
|
|
@@ -104,6 +114,12 @@ export interface NexusEventPayloads {
|
|
|
104
114
|
compressionPct: number;
|
|
105
115
|
durationMs: number;
|
|
106
116
|
result: string;
|
|
117
|
+
autoGhostPass?: {
|
|
118
|
+
applied: boolean;
|
|
119
|
+
riskAreas: string[];
|
|
120
|
+
workerApproaches: number;
|
|
121
|
+
estimatedTokens: number;
|
|
122
|
+
};
|
|
107
123
|
};
|
|
108
124
|
'session.summaryBootstrap': {
|
|
109
125
|
originalTokens: number;
|
|
@@ -1036,7 +1036,7 @@ export class OrchestratorEngine {
|
|
|
1036
1036
|
// Non-fatal: bootstrap receipt is a safety net, not a hard dependency
|
|
1037
1037
|
}
|
|
1038
1038
|
const [army, prepared] = await this.runParallelPhases(this.induce(task), this._prepareExecution(task, options));
|
|
1039
|
-
const { intent, phases, primaryClient, bootstrapManifest, latestDNA, memoryMatches, memoryStats, candidateFiles, knowledgeFabric, plannedFiles, planner, selections, catalogHealth, tokenBudget, workerCount, mode, taskGraph, workerPlan, autoGhostPass, crSignals, } = prepared;
|
|
1039
|
+
const { intent, phases, primaryClient, bootstrapManifest, latestDNA, memoryMatches, memoryStats, candidateFiles, knowledgeFabric, plannedFiles, planner, selections, catalogHealth, tokenBudget, workerCount, mode, taskGraph, workerPlan, autoGhostPass, autoGhostPassDecision, crSignals, } = prepared;
|
|
1040
1040
|
this.runtime.recordClientToolCall('nexus_orchestrate', {
|
|
1041
1041
|
orchestrateCalled: true,
|
|
1042
1042
|
plannerCalled: true,
|
|
@@ -1314,6 +1314,8 @@ export class OrchestratorEngine {
|
|
|
1314
1314
|
instructionPacket,
|
|
1315
1315
|
executionLedger: ledger,
|
|
1316
1316
|
knowledgeFabric,
|
|
1317
|
+
autoGhostPass,
|
|
1318
|
+
autoGhostPassDecision,
|
|
1317
1319
|
});
|
|
1318
1320
|
this.executionDedupeStore.set(fingerprint, {
|
|
1319
1321
|
id: ledger.runId,
|
|
@@ -24,6 +24,10 @@ export function getNexusHookSpec() {
|
|
|
24
24
|
matcher: 'Edit|Write|MultiEdit',
|
|
25
25
|
hooks: [{ type: 'command', command: 'nexus-prime hook mindkit', timeout: 10 }],
|
|
26
26
|
},
|
|
27
|
+
{
|
|
28
|
+
matcher: 'MultiEdit',
|
|
29
|
+
hooks: [{ type: 'command', command: 'nexus-prime hook ghost-pass', timeout: 15 }],
|
|
30
|
+
},
|
|
27
31
|
],
|
|
28
32
|
PostToolUse: [
|
|
29
33
|
{
|
|
@@ -101,6 +101,12 @@ export interface ExecutionTask {
|
|
|
101
101
|
instructionPacket?: InstructionPacket;
|
|
102
102
|
executionLedger?: ExecutionLedger;
|
|
103
103
|
knowledgeFabric?: KnowledgeFabricBundle;
|
|
104
|
+
autoGhostPass?: GhostReport;
|
|
105
|
+
autoGhostPassDecision?: {
|
|
106
|
+
enabled: boolean;
|
|
107
|
+
reason: string;
|
|
108
|
+
contextHash?: string;
|
|
109
|
+
};
|
|
104
110
|
}
|
|
105
111
|
export interface WorkerSkillOverlay {
|
|
106
112
|
base: string[];
|
package/dist/phantom/runtime.js
CHANGED
|
@@ -225,6 +225,16 @@ export class SubAgentRuntime {
|
|
|
225
225
|
files: (task.files ?? []).slice(0, 24),
|
|
226
226
|
workers: planner.plannerState.selectedSpecialists.length || 1,
|
|
227
227
|
phases: planner.plannerState.ledger.length,
|
|
228
|
+
autoGhostPass: {
|
|
229
|
+
applied: Boolean(task.autoGhostPass),
|
|
230
|
+
riskAreas: task.autoGhostPass?.riskAreas ?? [],
|
|
231
|
+
workerApproaches: task.autoGhostPass?.workerAssignments?.length ?? 0,
|
|
232
|
+
estimatedTokens: task.autoGhostPass?.totalEstimatedTokens ?? 0,
|
|
233
|
+
policy: task.autoGhostPassDecision ? {
|
|
234
|
+
enabled: task.autoGhostPassDecision.enabled,
|
|
235
|
+
reason: task.autoGhostPassDecision.reason,
|
|
236
|
+
} : undefined,
|
|
237
|
+
},
|
|
228
238
|
});
|
|
229
239
|
}
|
|
230
240
|
catch { /* best-effort */ }
|
|
@@ -765,6 +775,12 @@ export class SubAgentRuntime {
|
|
|
765
775
|
compressionPct: Number(tt?.compressionPct ?? 0),
|
|
766
776
|
durationMs: Date.now() - runStartedAt,
|
|
767
777
|
result: String(applied.summary ?? '').slice(0, 280),
|
|
778
|
+
autoGhostPass: {
|
|
779
|
+
applied: Boolean(task.autoGhostPass),
|
|
780
|
+
riskAreas: task.autoGhostPass?.riskAreas ?? [],
|
|
781
|
+
workerApproaches: task.autoGhostPass?.workerAssignments?.length ?? 0,
|
|
782
|
+
estimatedTokens: task.autoGhostPass?.totalEstimatedTokens ?? 0,
|
|
783
|
+
},
|
|
768
784
|
});
|
|
769
785
|
}
|
|
770
786
|
catch { /* best-effort */ }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-prime",
|
|
3
|
-
"version": "7.9.
|
|
3
|
+
"version": "7.9.26",
|
|
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",
|