nexus-prime 7.9.31 → 7.9.33
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/README.md +9 -9
- package/dist/agents/adapters/mcp/handlers/orchestration.js +6 -4
- package/dist/agents/adapters/mcp/handlers/runtime.js +3 -3
- package/dist/agents/adapters/mcp/stdio-buffer.js +10 -0
- package/dist/agents/adapters/mcp.js +3 -1
- package/dist/build-assets.d.ts +1 -0
- package/dist/build-assets.js +39 -0
- package/dist/cli.js +1 -1
- package/dist/dashboard/app/index.html +17 -12
- package/dist/dashboard/app/main.js +52 -0
- package/dist/dashboard/app/state.js +5 -0
- package/dist/dashboard/app/styles/board.css +157 -3
- package/dist/dashboard/app/styles/shell.css +19 -1
- package/dist/dashboard/app/styles/workforce.css +132 -0
- package/dist/dashboard/app/views/board.js +305 -12
- package/dist/dashboard/app/views/learning.js +42 -2
- package/dist/dashboard/app/views/license.js +3 -3
- package/dist/dashboard/app/views/memory.js +53 -11
- package/dist/dashboard/app/views/workforce.js +312 -21
- package/dist/dashboard/routes/governance.js +6 -1
- package/dist/dashboard/routes/graph.js +19 -5
- package/dist/dashboard/routes/runtime.js +78 -0
- package/dist/dashboard/routes/workforce.d.ts +1 -0
- package/dist/dashboard/routes/workforce.js +40 -0
- package/dist/dashboard/selectors/operate-selector.js +219 -2
- package/dist/dashboard/types.d.ts +1 -0
- package/dist/engines/dispatch/context-pack.d.ts +3 -1
- package/dist/engines/dispatch/context-pack.js +2 -0
- package/dist/engines/dispatch/event-mapper.js +17 -0
- package/dist/engines/dispatch/push-dispatch.d.ts +3 -0
- package/dist/engines/dispatch/push-dispatch.js +66 -7
- package/dist/engines/event-bus.d.ts +15 -0
- package/dist/engines/middleware-pipeline.js +3 -3
- package/dist/engines/model-pricing.d.ts +74 -0
- package/dist/engines/model-pricing.js +438 -0
- package/dist/engines/orchestrator/decision-spine.d.ts +65 -0
- package/dist/engines/orchestrator/decision-spine.js +368 -44
- package/dist/engines/providers/registry.js +18 -15
- package/dist/engines/providers/types.d.ts +9 -1
- package/dist/engines/specialist-cost-estimator.d.ts +2 -5
- package/dist/engines/specialist-cost-estimator.js +16 -10
- package/dist/engines/token-analytics.d.ts +15 -0
- package/dist/engines/token-analytics.js +32 -0
- package/dist/index.js +36 -19
- package/dist/invokers/aider.js +26 -4
- package/dist/invokers/base.d.ts +3 -0
- package/dist/invokers/base.js +41 -0
- package/dist/invokers/claude-code.js +24 -7
- package/dist/invokers/codex.js +24 -5
- package/dist/invokers/cursor.js +25 -4
- package/dist/invokers/types.d.ts +25 -0
- package/dist/licensing/enforcement.js +11 -12
- package/dist/licensing/license-manager.d.ts +4 -5
- package/dist/licensing/license-manager.js +22 -24
- package/dist/licensing/types.d.ts +1 -1
- package/dist/licensing/upgrade-prompts.d.ts +3 -4
- package/dist/licensing/upgrade-prompts.js +15 -18
- package/dist/licensing/web-auth.d.ts +2 -2
- package/dist/licensing/web-auth.js +4 -4
- package/dist/phantom/runtime/diff-preview.js +12 -0
- package/dist/phantom/runtime.d.ts +24 -1
- package/dist/phantom/runtime.js +125 -18
- package/dist/postinstall/cleanup.js +1 -1
- package/dist/postinstall-bootstrap.js +4 -4
- package/dist/postinstall-runner.d.ts +1 -0
- package/dist/postinstall-runner.js +18 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<p>
|
|
17
17
|
<img src="https://img.shields.io/badge/Protocol-MCP-4285F4?style=for-the-badge" alt="MCP Protocol">
|
|
18
18
|
<img src="https://img.shields.io/badge/license-Commercial-6f42c1?style=for-the-badge" alt="Commercial License">
|
|
19
|
-
<a href="https://nexus-prime.cfd/pricing"><img src="https://img.shields.io/badge/
|
|
19
|
+
<a href="https://nexus-prime.cfd/pricing"><img src="https://img.shields.io/badge/15--day-trial%20then%20license-00ff88?style=for-the-badge" alt="15-day trial then license"></a>
|
|
20
20
|
<img src="https://img.shields.io/badge/licenses-reviewed%20within%2024h-ff69b4?style=for-the-badge" alt="Licenses reviewed within 24 hours">
|
|
21
21
|
<img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-444?style=for-the-badge" alt="Cross-platform">
|
|
22
22
|
</p>
|
|
@@ -317,7 +317,7 @@ If any of that sounds like you — keep reading.
|
|
|
317
317
|
|
|
318
318
|
<div align="center">
|
|
319
319
|
|
|
320
|
-
**
|
|
320
|
+
**15-day local trial. Activate or upgrade before expiry; runtime work locks after day 15.**
|
|
321
321
|
|
|
322
322
|
</div>
|
|
323
323
|
|
|
@@ -329,10 +329,10 @@ If any of that sounds like you — keep reading.
|
|
|
329
329
|
<th>Caps</th>
|
|
330
330
|
</tr>
|
|
331
331
|
<tr>
|
|
332
|
-
<td><b>🎁 Local
|
|
332
|
+
<td><b>🎁 Local Trial</b></td>
|
|
333
333
|
<td>Everyone — install and verify locally</td>
|
|
334
|
-
<td>
|
|
335
|
-
<td>Runtime available during
|
|
334
|
+
<td>15 days without a license</td>
|
|
335
|
+
<td>Runtime available during trial; license required after day 15</td>
|
|
336
336
|
</tr>
|
|
337
337
|
<tr>
|
|
338
338
|
<td><b>💎 Independent Creators</b></td>
|
|
@@ -390,9 +390,9 @@ No — it supercharges them. Keep the agents you already love. Nexus Prime adds
|
|
|
390
390
|
</details>
|
|
391
391
|
|
|
392
392
|
<details>
|
|
393
|
-
<summary><b>What happens after the
|
|
393
|
+
<summary><b>What happens after the 15-day trial ends?</b></summary>
|
|
394
394
|
<br>
|
|
395
|
-
The local trial window ends and
|
|
395
|
+
The local trial window ends and Nexus Prime runtime work is locked until an active license is activated. Your local memory and data stay on your machine; upgrade, buy, or request renewal from the account page to continue.
|
|
396
396
|
</details>
|
|
397
397
|
|
|
398
398
|
<details>
|
|
@@ -445,7 +445,7 @@ Nexus Prime was designed privacy-first, because the code on your machine is your
|
|
|
445
445
|
|
|
446
446
|
- 🏠 **100% local.** All data lives on your disk, in your home directory.
|
|
447
447
|
- 🚫 **No cloud sync.** Your code, your memory, your logs — never uploaded.
|
|
448
|
-
- 🔐 **
|
|
448
|
+
- 🔐 **Bounded local trial.** You can run locally for 15 days without signing up; a license is required after the trial expires.
|
|
449
449
|
- 📊 **Telemetry is opt-in.** Off by default. When on, only aggregate, anonymous event counts are sent — never source code, never memory contents.
|
|
450
450
|
- 🗑️ **Uninstall is clean.** Everything lives in one directory you can delete.
|
|
451
451
|
|
|
@@ -473,7 +473,7 @@ Nexus Prime was designed privacy-first, because the code on your machine is your
|
|
|
473
473
|
|
|
474
474
|
Nexus Prime is a **commercial product** with manual license issuance:
|
|
475
475
|
|
|
476
|
-
- ✅
|
|
476
|
+
- ✅ 15-day local trial, for everyone
|
|
477
477
|
- ✅ Manual creator, pilot, team, and enterprise license review
|
|
478
478
|
- ✅ License and upgrade requests reviewed within 24 hours
|
|
479
479
|
- 💳 Paid plans for professional, team, and enterprise usage
|
|
@@ -21,6 +21,7 @@ import { getSharedTelemetry } from '../../../../engines/telemetry-remote.js';
|
|
|
21
21
|
import { requireRuntime } from '../util/require-runtime.js';
|
|
22
22
|
import { ensureCrGraphBuilt } from '../../../../engines/code-review-graph-client.js';
|
|
23
23
|
import { recordFirstBootstrap } from '../../../../engines/telemetry.js';
|
|
24
|
+
import { estimateModelCostUsd } from '../../../../engines/model-pricing.js';
|
|
24
25
|
function escapeMarkdownTableCell(value) {
|
|
25
26
|
return String(value ?? '')
|
|
26
27
|
.replace(/\r?\n/g, ' ')
|
|
@@ -122,6 +123,8 @@ function buildSelectionSummary(execution, runtimeUsage) {
|
|
|
122
123
|
automations: asStringList(selected.automations, 8),
|
|
123
124
|
workers,
|
|
124
125
|
modelRoute: selectionPlan.modelRoute ?? execution.requestBrief?.modelPolicy,
|
|
126
|
+
preferencePolicy: selectionPlan.preferencePolicy ?? execution.requestBrief?.preferencePolicy,
|
|
127
|
+
podNetwork: selectionPlan.podNetwork,
|
|
125
128
|
budgetRoute: formatBudgetRoute(selectionPlan),
|
|
126
129
|
agentFlow: selectionPlan.executionPolicy?.agentFlow,
|
|
127
130
|
phaseCount: Array.isArray(taskGraph.phases) ? taskGraph.phases.length : 0,
|
|
@@ -802,7 +805,7 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
802
805
|
tokensForwarded: plan.totalEstimatedTokens,
|
|
803
806
|
compressionRatio: grossInput > 0 ? plan.totalEstimatedTokens / grossInput : 0,
|
|
804
807
|
fileCount: filePaths.length,
|
|
805
|
-
usdValueSaved: plan.savings
|
|
808
|
+
usdValueSaved: estimateModelCostUsd('claude-sonnet-4-6', plan.savings, 0),
|
|
806
809
|
});
|
|
807
810
|
}
|
|
808
811
|
}
|
|
@@ -811,9 +814,8 @@ export async function handleOrchestrationGroup(toolName, hctx, request, args, ct
|
|
|
811
814
|
const fullReads = plan.files.filter((a) => a.action === 'full').length;
|
|
812
815
|
const nudge = hctx.telemetry.planningNudge('optimize', { fullReads });
|
|
813
816
|
// Per-call receipt: honest token + cost accounting (dedup shown separately)
|
|
814
|
-
const
|
|
815
|
-
const
|
|
816
|
-
const savedCostUsd = (plan.savings / 1_000) * COST_PER_1K_IN;
|
|
817
|
+
const estimatedCostUsd = estimateModelCostUsd('claude-sonnet-4-6', plan.totalEstimatedTokens, 0);
|
|
818
|
+
const savedCostUsd = estimateModelCostUsd('claude-sonnet-4-6', plan.savings, 0);
|
|
817
819
|
const dedupLine = dedupTokens > 0
|
|
818
820
|
? `\n ${dedupTokens.toLocaleString()} tok deduped (${delta.unchanged.length} unchanged files)`
|
|
819
821
|
: '';
|
|
@@ -44,10 +44,10 @@ export async function handleRuntimeGroup(toolName, hctx, request, args, ctx) {
|
|
|
44
44
|
? `- **Trial remaining**: ${licStatus.trialDaysRemaining} day${licStatus.trialDaysRemaining === 1 ? '' : 's'}`
|
|
45
45
|
: '',
|
|
46
46
|
licStatus.activationRequired
|
|
47
|
-
? `- **Agent motion**:
|
|
47
|
+
? `- **Agent motion**: 15-day trial is active; ask the user to activate or upgrade before expiry.`
|
|
48
48
|
: licStatus.trialPhase === 'activation'
|
|
49
|
-
? `- **Agent motion**: Trial remains active; ask the user to request or activate a license soon, then keep working.`
|
|
50
|
-
: `- **Agent motion**:
|
|
49
|
+
? `- **Agent motion**: Trial remains active; ask the user to request or activate a license soon, then keep working until expiry.`
|
|
50
|
+
: `- **Agent motion**: Trial remains active; activate before the 15-day window ends.`,
|
|
51
51
|
].filter((line) => Boolean(line)) : [];
|
|
52
52
|
const lines = [
|
|
53
53
|
`## License Status`,
|
|
@@ -4,16 +4,24 @@ let primedStdioInputStarted = false;
|
|
|
4
4
|
let primedStdioInputReady = false;
|
|
5
5
|
let primedStdioInputEnded = false;
|
|
6
6
|
const primedStdioChunks = [];
|
|
7
|
+
function debugStdioBuffer(message) {
|
|
8
|
+
if (process.env.NEXUS_DEBUG_STDIO_BUFFER === '1') {
|
|
9
|
+
console.error(`[MCP stdio buffer] ${message}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
7
12
|
export function primeMcpStdioInput() {
|
|
8
13
|
if (primedStdioInputStarted || process.stdin.isTTY)
|
|
9
14
|
return;
|
|
10
15
|
primedStdioInputStarted = true;
|
|
16
|
+
debugStdioBuffer('primed');
|
|
11
17
|
process.stdin.on('data', (chunk) => {
|
|
12
18
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
13
19
|
if (primedStdioInput && primedStdioInputReady) {
|
|
20
|
+
debugStdioBuffer(`forward ${buffer.length} bytes`);
|
|
14
21
|
primedStdioInput.write(buffer);
|
|
15
22
|
return;
|
|
16
23
|
}
|
|
24
|
+
debugStdioBuffer(`capture ${buffer.length} bytes`);
|
|
17
25
|
primedStdioChunks.push(buffer);
|
|
18
26
|
});
|
|
19
27
|
process.stdin.on('end', () => {
|
|
@@ -27,6 +35,7 @@ export function primeMcpStdioInput() {
|
|
|
27
35
|
export function getMcpStdioInput() {
|
|
28
36
|
if (primedStdioInputStarted && !primedStdioInput) {
|
|
29
37
|
primedStdioInput = new PassThrough();
|
|
38
|
+
debugStdioBuffer('created passthrough');
|
|
30
39
|
}
|
|
31
40
|
return primedStdioInput ?? process.stdin;
|
|
32
41
|
}
|
|
@@ -34,6 +43,7 @@ export function flushPrimedMcpStdioInput() {
|
|
|
34
43
|
if (!primedStdioInput)
|
|
35
44
|
return;
|
|
36
45
|
primedStdioInputReady = true;
|
|
46
|
+
debugStdioBuffer(`flush ${primedStdioChunks.length} chunks`);
|
|
37
47
|
for (const chunk of primedStdioChunks.splice(0)) {
|
|
38
48
|
primedStdioInput.write(chunk);
|
|
39
49
|
}
|
|
@@ -30,7 +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
|
+
import { flushPrimedMcpStdioInput, getMcpStdioInput } from './mcp/stdio-buffer.js';
|
|
34
34
|
// Derive project root from this file's location (dist/agents/adapters/mcp.js → project root)
|
|
35
35
|
const __filename = fileURLToPath(import.meta.url);
|
|
36
36
|
const PROJECT_ROOT = path.resolve(path.dirname(__filename), '..', '..', '..');
|
|
@@ -1170,6 +1170,8 @@ export class MCPAdapter {
|
|
|
1170
1170
|
const transport = new StdioServerTransport(getMcpStdioInput(), process.stdout);
|
|
1171
1171
|
await this.server.connect(transport);
|
|
1172
1172
|
this.connected = true;
|
|
1173
|
+
flushPrimedMcpStdioInput();
|
|
1174
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1173
1175
|
startWatchdog();
|
|
1174
1176
|
console.error('[MCP Adapter] Connected — runtime tools active');
|
|
1175
1177
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
const root = process.cwd();
|
|
4
|
+
const dist = path.join(root, 'dist');
|
|
5
|
+
function ensureDir(dir) {
|
|
6
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
function copyDir(from, to) {
|
|
9
|
+
if (!fs.existsSync(from))
|
|
10
|
+
return;
|
|
11
|
+
fs.rmSync(to, { recursive: true, force: true });
|
|
12
|
+
ensureDir(path.dirname(to));
|
|
13
|
+
fs.cpSync(from, to, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
function copyFile(from, to) {
|
|
16
|
+
if (!fs.existsSync(from))
|
|
17
|
+
return;
|
|
18
|
+
ensureDir(path.dirname(to));
|
|
19
|
+
fs.copyFileSync(from, to);
|
|
20
|
+
}
|
|
21
|
+
function copyJsonFiles(fromDir, toDir) {
|
|
22
|
+
if (!fs.existsSync(fromDir))
|
|
23
|
+
return;
|
|
24
|
+
ensureDir(toDir);
|
|
25
|
+
for (const entry of fs.readdirSync(fromDir)) {
|
|
26
|
+
if (entry.endsWith('.json'))
|
|
27
|
+
copyFile(path.join(fromDir, entry), path.join(toDir, entry));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
copyDir(path.join(root, 'src', 'dashboard', 'app'), path.join(dist, 'dashboard', 'app'));
|
|
31
|
+
copyFile(path.join(root, 'src', 'dashboard', 'welcome.html'), path.join(dist, 'dashboard', 'welcome.html'));
|
|
32
|
+
copyJsonFiles(path.join(root, 'src', 'engines', 'data'), path.join(dist, 'engines', 'data'));
|
|
33
|
+
copyDir(path.join(root, 'src', 'migrations'), path.join(dist, 'migrations'));
|
|
34
|
+
try {
|
|
35
|
+
fs.chmodSync(path.join(dist, 'cli.js'), 0o755);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Windows/global npm shims do not require POSIX executable bits.
|
|
39
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1126,9 +1126,9 @@ program
|
|
|
1126
1126
|
throw new Error('MCP adapter did not become ready before the startup deadline.');
|
|
1127
1127
|
}
|
|
1128
1128
|
flushPrimedMcpStdioInput();
|
|
1129
|
-
await startup;
|
|
1130
1129
|
console.error('Nexus Prime MCP Server running on stdio (standalone)');
|
|
1131
1130
|
console.error('Memory persistence: active (~/.nexus-prime/memory.db)');
|
|
1131
|
+
await startup;
|
|
1132
1132
|
const shutdown = async (signal) => {
|
|
1133
1133
|
nexusEventBus.emit('nexus.shutdown', { signal });
|
|
1134
1134
|
nexus?.getOrchestrator().dispose();
|
|
@@ -40,6 +40,9 @@ if(location.protocol==='file:'){
|
|
|
40
40
|
<div class="sidebar-brand">
|
|
41
41
|
<div class="brand-mark" aria-hidden="true"></div>
|
|
42
42
|
<span class="brand-name">Nexus Prime</span>
|
|
43
|
+
<button id="sidebar-collapse-btn" class="sidebar-collapse-btn" type="button" aria-label="Collapse navigation" aria-expanded="true" title="Collapse navigation">
|
|
44
|
+
<span class="sidebar-collapse-icon" aria-hidden="true"><</span>
|
|
45
|
+
</button>
|
|
43
46
|
</div>
|
|
44
47
|
<div class="sidebar-workspace" id="workspace-badge" title="Active workspace">
|
|
45
48
|
<span class="ws-repo" id="workspace-repo">…</span>
|
|
@@ -53,18 +56,18 @@ if(location.protocol==='file:'){
|
|
|
53
56
|
</div>
|
|
54
57
|
|
|
55
58
|
<nav id="tab-nav" aria-label="Primary navigation">
|
|
56
|
-
<a class="nav-item active" data-nav="board" href="#board">Board</a>
|
|
57
|
-
<a class="nav-item" data-nav="runtime" href="#runtime">⚡ Runtime</a>
|
|
58
|
-
<a class="nav-item" data-nav="workforce" href="#workforce">Workforce</a>
|
|
59
|
-
<a class="nav-item" data-nav="memory" href="#memory">Memory</a>
|
|
60
|
-
<a class="nav-item" data-nav="learning" href="#learning">Learning</a>
|
|
61
|
-
<a class="nav-item" data-nav="context-log" href="#context-log">Context Log</a>
|
|
62
|
-
<a class="nav-item" data-nav="repo" href="#repo">Repo</a>
|
|
63
|
-
<a class="nav-item" data-nav="knowledge" href="#knowledge">Knowledge</a>
|
|
64
|
-
<a class="nav-item" data-nav="governance" href="#governance">Governance</a>
|
|
65
|
-
<a class="nav-item" data-nav="trust" href="#trust">Trust</a>
|
|
66
|
-
<a class="nav-item" data-nav="license" href="#license">License</a>
|
|
67
|
-
<a class="nav-item" data-nav="federation" href="#federation">Federation</a>
|
|
59
|
+
<a class="nav-item active" data-nav="board" data-short="B" title="Board" href="#board">Board</a>
|
|
60
|
+
<a class="nav-item" data-nav="runtime" data-short="R" title="Runtime" href="#runtime">⚡ Runtime</a>
|
|
61
|
+
<a class="nav-item" data-nav="workforce" data-short="W" title="Workforce" href="#workforce">Workforce</a>
|
|
62
|
+
<a class="nav-item" data-nav="memory" data-short="M" title="Memory" href="#memory">Memory</a>
|
|
63
|
+
<a class="nav-item" data-nav="learning" data-short="L" title="Learning" href="#learning">Learning</a>
|
|
64
|
+
<a class="nav-item" data-nav="context-log" data-short="C" title="Context Log" href="#context-log">Context Log</a>
|
|
65
|
+
<a class="nav-item" data-nav="repo" data-short="P" title="Repo" href="#repo">Repo</a>
|
|
66
|
+
<a class="nav-item" data-nav="knowledge" data-short="K" title="Knowledge" href="#knowledge">Knowledge</a>
|
|
67
|
+
<a class="nav-item" data-nav="governance" data-short="G" title="Governance" href="#governance">Governance</a>
|
|
68
|
+
<a class="nav-item" data-nav="trust" data-short="T" title="Trust" href="#trust">Trust</a>
|
|
69
|
+
<a class="nav-item" data-nav="license" data-short="$" title="License" href="#license">License</a>
|
|
70
|
+
<a class="nav-item" data-nav="federation" data-short="F" title="Federation" href="#federation">Federation</a>
|
|
68
71
|
</nav>
|
|
69
72
|
|
|
70
73
|
<div class="sidebar-foot">
|
|
@@ -166,6 +169,8 @@ if(location.protocol==='file:'){
|
|
|
166
169
|
<section class="view-panel" data-view="workforce" aria-label="Workforce">
|
|
167
170
|
<div class="shd">Operative org chart</div>
|
|
168
171
|
<div id="org-container" class="card"></div>
|
|
172
|
+
<div class="shd">Execution network</div>
|
|
173
|
+
<div id="workforce-execution-network" class="card execution-network"></div>
|
|
169
174
|
<div class="workforce-bottom">
|
|
170
175
|
<div><div class="shd">Active missions</div><div class="card" id="mission-list"></div></div>
|
|
171
176
|
<div><div class="shd">Dispatch</div><div class="card" id="dispatch-panel"></div></div>
|
|
@@ -29,7 +29,9 @@ import { mount as mountRuntimeBadges } from './widgets/runtime-badge.js';
|
|
|
29
29
|
|
|
30
30
|
const $ = id => document.getElementById(id);
|
|
31
31
|
const esc = s => s==null?'':String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
32
|
+
const SIDEBAR_COLLAPSED_KEY = 'nexus.sidebarCollapsed';
|
|
32
33
|
let memoryReloadTimer = null;
|
|
34
|
+
let workforceReloadTimer = null;
|
|
33
35
|
|
|
34
36
|
function _memoryApiUrl() {
|
|
35
37
|
const params = new URLSearchParams({ includeInactive: 'true', limit: '100' });
|
|
@@ -50,6 +52,47 @@ function _refreshMemorySoon() {
|
|
|
50
52
|
}, 150);
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
function _refreshWorkforceSoon(forceLoad = false) {
|
|
56
|
+
bustCache('/api/workforce/kanban');
|
|
57
|
+
bustCache('/api/synapse/health');
|
|
58
|
+
bustCache('/api/dashboard/surface/operate');
|
|
59
|
+
if (S.tab === 'workforce') Workforce.render();
|
|
60
|
+
if (!forceLoad || S.tab !== 'workforce') return;
|
|
61
|
+
clearTimeout(workforceReloadTimer);
|
|
62
|
+
workforceReloadTimer = setTimeout(() => {
|
|
63
|
+
workforceReloadTimer = null;
|
|
64
|
+
Workforce.load();
|
|
65
|
+
}, 300);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function _applySidebarCollapsed(collapsed) {
|
|
69
|
+
document.body.classList.toggle('sidebar-collapsed', collapsed);
|
|
70
|
+
const btn = $('sidebar-collapse-btn');
|
|
71
|
+
if (!btn) return;
|
|
72
|
+
const expanded = !collapsed;
|
|
73
|
+
btn.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
|
74
|
+
btn.setAttribute('aria-label', expanded ? 'Collapse navigation' : 'Expand navigation');
|
|
75
|
+
btn.title = expanded ? 'Collapse navigation' : 'Expand navigation';
|
|
76
|
+
const icon = btn.querySelector('.sidebar-collapse-icon');
|
|
77
|
+
if (icon) icon.textContent = expanded ? '<' : '>';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function _initSidebarCollapse() {
|
|
81
|
+
document.querySelectorAll('#tab-nav .nav-item').forEach(item => {
|
|
82
|
+
if (!item.title) item.title = item.textContent.trim();
|
|
83
|
+
if (!item.dataset.short) item.dataset.short = (item.textContent.trim()[0] || '?').toUpperCase();
|
|
84
|
+
});
|
|
85
|
+
let stored = null;
|
|
86
|
+
try { stored = localStorage.getItem(SIDEBAR_COLLAPSED_KEY); } catch { stored = null; }
|
|
87
|
+
const defaultCollapsed = stored == null && window.matchMedia?.('(max-width: 760px)').matches;
|
|
88
|
+
_applySidebarCollapsed(stored === '1' || defaultCollapsed);
|
|
89
|
+
$('sidebar-collapse-btn')?.addEventListener('click', () => {
|
|
90
|
+
const collapsed = !document.body.classList.contains('sidebar-collapsed');
|
|
91
|
+
try { localStorage.setItem(SIDEBAR_COLLAPSED_KEY, collapsed ? '1' : '0'); } catch { /* ignore private-mode storage failures */ }
|
|
92
|
+
_applySidebarCollapsed(collapsed);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
53
96
|
/* ─────────────────── Router ─────────────────── */
|
|
54
97
|
navRegister('board', Board.load);
|
|
55
98
|
navRegister('runtime', Runtime.load);
|
|
@@ -114,6 +157,12 @@ setOnEvent(evt => {
|
|
|
114
157
|
// Route push-mode dispatch events regardless of active tab
|
|
115
158
|
if (String(evt.type||'').startsWith('dispatch.')) {
|
|
116
159
|
Workforce.handleDispatchEvent(evt);
|
|
160
|
+
const payload = evt.payload ?? evt.data ?? {};
|
|
161
|
+
const kind = String(payload.kind || '');
|
|
162
|
+
const shouldLoad = ['dispatch.started','dispatch.complete','dispatch.failed','dispatch.cancelled'].includes(String(evt.type||''))
|
|
163
|
+
|| ['done','error','file-change'].includes(kind);
|
|
164
|
+
_refreshWorkforceSoon(shouldLoad);
|
|
165
|
+
if (tab === 'board') Board.render();
|
|
117
166
|
}
|
|
118
167
|
|
|
119
168
|
// Orchestration pipeline events — keep board fresh as decomposition / completion fires.
|
|
@@ -282,6 +331,9 @@ function _attachHandlers() {
|
|
|
282
331
|
// Command bar
|
|
283
332
|
initCmdBar();
|
|
284
333
|
|
|
334
|
+
// Collapsible navigation rail
|
|
335
|
+
_initSidebarCollapse();
|
|
336
|
+
|
|
285
337
|
// Memory view init
|
|
286
338
|
Memory.init();
|
|
287
339
|
|
|
@@ -31,6 +31,8 @@ export const S = {
|
|
|
31
31
|
// Board / cockpit
|
|
32
32
|
tokensSummary: null,
|
|
33
33
|
tokensLifetime: null,
|
|
34
|
+
tokenEconomics: null,
|
|
35
|
+
providerEconomics: null,
|
|
34
36
|
operateSurface: null,
|
|
35
37
|
synapseHealth: [],
|
|
36
38
|
synapseHealthRaw: null,
|
|
@@ -68,6 +70,9 @@ export const S = {
|
|
|
68
70
|
selectedMemoryIds: [],
|
|
69
71
|
memorySurface: null,
|
|
70
72
|
topology: null,
|
|
73
|
+
memoryLoading: false,
|
|
74
|
+
memoryError: null,
|
|
75
|
+
memoryLoadedAt:null,
|
|
71
76
|
|
|
72
77
|
// Knowledge
|
|
73
78
|
ragCollections: [],
|
|
@@ -43,12 +43,15 @@
|
|
|
43
43
|
}
|
|
44
44
|
.model-router-lanes {
|
|
45
45
|
display: grid;
|
|
46
|
-
grid-template-columns: repeat(
|
|
46
|
+
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
|
|
47
47
|
gap: 10px;
|
|
48
48
|
}
|
|
49
49
|
.model-lane {
|
|
50
|
-
min-height:
|
|
50
|
+
min-height: 124px;
|
|
51
51
|
padding: 12px;
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: 3px;
|
|
52
55
|
text-align: left;
|
|
53
56
|
color: var(--text-muted);
|
|
54
57
|
background: rgba(0,0,0,0.2);
|
|
@@ -69,10 +72,17 @@
|
|
|
69
72
|
color: var(--text-main);
|
|
70
73
|
font-family: var(--font-mono);
|
|
71
74
|
font-size: 1rem;
|
|
75
|
+
overflow-wrap: anywhere;
|
|
72
76
|
}
|
|
73
77
|
.model-lane small {
|
|
78
|
+
display: block;
|
|
74
79
|
color: var(--text-dim);
|
|
75
80
|
font-family: var(--font-mono);
|
|
81
|
+
line-height: 1.35;
|
|
82
|
+
overflow-wrap: anywhere;
|
|
83
|
+
}
|
|
84
|
+
.model-lane-purpose {
|
|
85
|
+
color: var(--text-muted);
|
|
76
86
|
}
|
|
77
87
|
.model-router-reason {
|
|
78
88
|
margin-top: 10px;
|
|
@@ -81,11 +91,155 @@
|
|
|
81
91
|
line-height: 1.45;
|
|
82
92
|
}
|
|
83
93
|
|
|
94
|
+
.token-economics-panel {
|
|
95
|
+
margin: 0 0 16px;
|
|
96
|
+
padding: 14px;
|
|
97
|
+
background: var(--bg-elevated);
|
|
98
|
+
}
|
|
99
|
+
.token-econ-head {
|
|
100
|
+
display: flex;
|
|
101
|
+
justify-content: space-between;
|
|
102
|
+
align-items: flex-start;
|
|
103
|
+
gap: 12px;
|
|
104
|
+
margin-bottom: 12px;
|
|
105
|
+
}
|
|
106
|
+
.token-econ-head span,
|
|
107
|
+
.token-econ-subhead {
|
|
108
|
+
display: block;
|
|
109
|
+
color: var(--text-dim);
|
|
110
|
+
font-family: var(--font-mono);
|
|
111
|
+
font-size: 0.68rem;
|
|
112
|
+
text-transform: uppercase;
|
|
113
|
+
letter-spacing: 0.06em;
|
|
114
|
+
}
|
|
115
|
+
.token-econ-head strong {
|
|
116
|
+
display: block;
|
|
117
|
+
margin-top: 3px;
|
|
118
|
+
color: var(--text-main);
|
|
119
|
+
font: var(--title);
|
|
120
|
+
}
|
|
121
|
+
.token-econ-proof {
|
|
122
|
+
color: var(--text-muted);
|
|
123
|
+
font-family: var(--font-mono);
|
|
124
|
+
font-size: 0.72rem;
|
|
125
|
+
text-align: right;
|
|
126
|
+
}
|
|
127
|
+
.token-econ-metrics {
|
|
128
|
+
display: grid;
|
|
129
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
130
|
+
gap: 1px;
|
|
131
|
+
margin-bottom: 12px;
|
|
132
|
+
background: var(--border);
|
|
133
|
+
border: 1px solid var(--border);
|
|
134
|
+
border-radius: var(--radius);
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
}
|
|
137
|
+
.token-econ-metrics div {
|
|
138
|
+
padding: 10px 12px;
|
|
139
|
+
background: rgba(0,0,0,0.22);
|
|
140
|
+
}
|
|
141
|
+
.token-econ-metrics span {
|
|
142
|
+
display: block;
|
|
143
|
+
color: var(--text-dim);
|
|
144
|
+
font-family: var(--font-mono);
|
|
145
|
+
font-size: 0.68rem;
|
|
146
|
+
text-transform: uppercase;
|
|
147
|
+
letter-spacing: 0.05em;
|
|
148
|
+
}
|
|
149
|
+
.token-econ-metrics strong {
|
|
150
|
+
display: block;
|
|
151
|
+
margin-top: 4px;
|
|
152
|
+
color: var(--text-main);
|
|
153
|
+
font-family: var(--font-mono);
|
|
154
|
+
font-size: 1rem;
|
|
155
|
+
}
|
|
156
|
+
.token-econ-grid {
|
|
157
|
+
display: grid;
|
|
158
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
159
|
+
gap: 10px;
|
|
160
|
+
}
|
|
161
|
+
.token-econ-subhead { margin-bottom: 6px; }
|
|
162
|
+
.token-econ-rows { display: flex; flex-direction: column; gap: 6px; }
|
|
163
|
+
.econ-row {
|
|
164
|
+
display: grid;
|
|
165
|
+
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
166
|
+
gap: 8px;
|
|
167
|
+
align-items: center;
|
|
168
|
+
min-height: 54px;
|
|
169
|
+
padding: 9px 10px;
|
|
170
|
+
border: 1px solid var(--border);
|
|
171
|
+
border-left: 2px solid rgba(0,255,136,0.32);
|
|
172
|
+
border-radius: var(--radius);
|
|
173
|
+
background: rgba(0,0,0,0.18);
|
|
174
|
+
}
|
|
175
|
+
.econ-row.live { border-left-color: rgba(0,212,255,0.36); }
|
|
176
|
+
.econ-row.verify.pass { border-left-color: rgba(0,255,136,0.46); }
|
|
177
|
+
.econ-row.verify.fail { border-left-color: rgba(255,95,87,0.46); }
|
|
178
|
+
.econ-row strong {
|
|
179
|
+
display: block;
|
|
180
|
+
color: var(--text-main);
|
|
181
|
+
font-family: var(--font-mono);
|
|
182
|
+
font-size: 0.78rem;
|
|
183
|
+
white-space: nowrap;
|
|
184
|
+
overflow: hidden;
|
|
185
|
+
text-overflow: ellipsis;
|
|
186
|
+
}
|
|
187
|
+
.econ-row span,
|
|
188
|
+
.econ-row > div:nth-child(2),
|
|
189
|
+
.econ-row > div:nth-child(3) {
|
|
190
|
+
color: var(--text-dim);
|
|
191
|
+
font-family: var(--font-mono);
|
|
192
|
+
font-size: 0.72rem;
|
|
193
|
+
}
|
|
194
|
+
.econ-row > div:nth-child(2),
|
|
195
|
+
.econ-row > div:nth-child(3) {
|
|
196
|
+
color: var(--text-muted);
|
|
197
|
+
white-space: nowrap;
|
|
198
|
+
}
|
|
199
|
+
.econ-empty {
|
|
200
|
+
min-height: 54px;
|
|
201
|
+
display: flex;
|
|
202
|
+
align-items: center;
|
|
203
|
+
justify-content: center;
|
|
204
|
+
padding: 10px;
|
|
205
|
+
color: var(--text-dim);
|
|
206
|
+
font-family: var(--font-mono);
|
|
207
|
+
font-size: 0.74rem;
|
|
208
|
+
border: 1px dashed var(--border);
|
|
209
|
+
border-radius: var(--radius);
|
|
210
|
+
}
|
|
211
|
+
|
|
84
212
|
@media (max-width: 900px) {
|
|
85
213
|
#hero-stats,
|
|
86
|
-
.model-router-lanes
|
|
214
|
+
.model-router-lanes,
|
|
215
|
+
.token-econ-metrics,
|
|
216
|
+
.token-econ-grid {
|
|
87
217
|
grid-template-columns: 1fr;
|
|
88
218
|
}
|
|
219
|
+
.token-econ-head { flex-direction: column; }
|
|
220
|
+
.token-econ-proof { text-align: left; }
|
|
221
|
+
.token-economics-panel,
|
|
222
|
+
.token-econ-grid > div,
|
|
223
|
+
.token-econ-rows {
|
|
224
|
+
min-width: 0;
|
|
225
|
+
overflow-x: hidden;
|
|
226
|
+
}
|
|
227
|
+
.econ-row {
|
|
228
|
+
grid-template-columns: 1fr;
|
|
229
|
+
align-items: flex-start;
|
|
230
|
+
box-sizing: border-box;
|
|
231
|
+
min-width: 0;
|
|
232
|
+
width: 100%;
|
|
233
|
+
}
|
|
234
|
+
.econ-row strong,
|
|
235
|
+
.econ-row span {
|
|
236
|
+
white-space: normal;
|
|
237
|
+
overflow-wrap: anywhere;
|
|
238
|
+
}
|
|
239
|
+
.econ-row > div:nth-child(2),
|
|
240
|
+
.econ-row > div:nth-child(3) {
|
|
241
|
+
white-space: normal;
|
|
242
|
+
}
|
|
89
243
|
}
|
|
90
244
|
|
|
91
245
|
/* ── Kanban ── */
|
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
#app { position:relative;z-index:1;display:flex;flex-direction:row;height:100vh; }
|
|
13
13
|
|
|
14
14
|
/* ── Sidebar ── */
|
|
15
|
-
#sidebar { flex-shrink:0;width:var(--sidebar-w);height:100vh;display:flex;flex-direction:column;border-right:1px solid var(--border);background:rgba(0,0,0,.9);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);z-index:10;overflow:hidden; }
|
|
15
|
+
#sidebar { flex-shrink:0;width:var(--sidebar-w);height:100vh;display:flex;flex-direction:column;border-right:1px solid var(--border);background:rgba(0,0,0,.9);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);z-index:10;overflow:hidden;transition:width .18s var(--ease); }
|
|
16
16
|
.sidebar-brand { display:flex;align-items:center;gap:10px;padding:16px 14px 14px;flex-shrink:0;border-bottom:1px solid var(--border); }
|
|
17
17
|
.brand-mark { width:13px;height:13px;border:1.5px solid var(--accent);transform:rotate(45deg);box-shadow:0 0 10px var(--accent-glow);flex-shrink:0; }
|
|
18
18
|
.brand-name { font-family:var(--font-mono);font-size:.78rem;font-weight:600;letter-spacing:.06em;color:var(--text-main); }
|
|
19
|
+
.sidebar-collapse-btn { margin-left:auto;width:26px;height:26px;display:flex;align-items:center;justify-content:center;color:var(--text-dim);border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-panel);font-family:var(--font-mono);font-size:.8rem;line-height:1;cursor:pointer;transition:color .15s,border-color .15s,background .15s; }
|
|
20
|
+
.sidebar-collapse-btn:hover { color:var(--accent);border-color:rgba(0,255,136,.3);background:rgba(0,255,136,.06); }
|
|
19
21
|
.sidebar-workspace { padding:8px 14px 12px;border-bottom:1px solid var(--border);display:flex;flex-direction:column;gap:2px;flex-shrink:0;min-height:44px; }
|
|
20
22
|
.sidebar-workspace .ws-repo { font-family:var(--font-mono);font-size:.78rem;font-weight:600;color:var(--text-main);letter-spacing:.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
|
|
21
23
|
.sidebar-workspace .ws-branch { font-family:var(--font-mono);font-size:.78rem;color:var(--text-muted);letter-spacing:.04em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
|
|
@@ -28,6 +30,22 @@
|
|
|
28
30
|
.nav-item:hover { color:var(--text-muted);background:rgba(255,255,255,.04); }
|
|
29
31
|
.nav-item.active { color:var(--accent);background:rgba(0,255,136,.07); }
|
|
30
32
|
.nav-item.active::before { content:'';position:absolute;left:0;top:2px;bottom:2px;width:2px;background:var(--accent);border-radius:0 1px 1px 0; }
|
|
33
|
+
body.sidebar-collapsed #sidebar { width:56px; }
|
|
34
|
+
body.sidebar-collapsed .sidebar-brand { justify-content:center;gap:6px;padding:16px 6px 14px; }
|
|
35
|
+
body.sidebar-collapsed .brand-name,
|
|
36
|
+
body.sidebar-collapsed .sidebar-workspace,
|
|
37
|
+
body.sidebar-collapsed .cmd-bar-wrap,
|
|
38
|
+
body.sidebar-collapsed #runtime-badges,
|
|
39
|
+
body.sidebar-collapsed .chat-toggle-btn span:last-child,
|
|
40
|
+
body.sidebar-collapsed #sse-label { display:none; }
|
|
41
|
+
body.sidebar-collapsed .sidebar-collapse-btn { margin-left:0;width:24px;height:24px; }
|
|
42
|
+
body.sidebar-collapsed #tab-nav { padding:8px 0; }
|
|
43
|
+
body.sidebar-collapsed .nav-item { justify-content:center;min-height:34px;padding:7px 0;font-size:0;letter-spacing:0; }
|
|
44
|
+
body.sidebar-collapsed .nav-item::after { content:attr(data-short);font-family:var(--font-mono);font-size:.74rem;font-weight:600;letter-spacing:0;color:inherit; }
|
|
45
|
+
body.sidebar-collapsed .nav-item.active::before { top:5px;bottom:5px; }
|
|
46
|
+
body.sidebar-collapsed .sidebar-foot { align-items:center;padding:8px; }
|
|
47
|
+
body.sidebar-collapsed .chat-toggle-btn { width:34px;height:32px;padding:0;justify-content:center; }
|
|
48
|
+
body.sidebar-collapsed .sse-pill { width:26px;height:26px;justify-content:center;padding:0;border-radius:999px; }
|
|
31
49
|
/* ── Command bar ── */
|
|
32
50
|
.cmd-bar-wrap { display:flex;gap:6px;padding:10px 14px;border-bottom:1px solid var(--border);flex-shrink:0; }
|
|
33
51
|
.cmd-input { flex:1;background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--radius);padding:4px 8px;font-family:var(--font-mono);font-size:.78rem;color:var(--text-main);outline:none;min-width:0; }
|