agent-nuvira 3.5.5 → 3.5.7
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/.agents/skills/index.json +16 -0
- package/.agents/skills/motion/SKILL.md +27 -0
- package/README.md +122 -1
- package/dist/cli/chat.d.ts +76 -17
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +330 -137
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +17 -3
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/doctor.d.ts +31 -2
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +143 -7
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/execute.d.ts.map +1 -1
- package/dist/cli/execute.js +63 -22
- package/dist/cli/execute.js.map +1 -1
- package/dist/cli/failover-runner.js +4 -4
- package/dist/cli/failover-runner.js.map +1 -1
- package/dist/cli/gateway.js +13 -13
- package/dist/cli/gateway.js.map +1 -1
- package/dist/cli/loop-executor.d.ts +22 -0
- package/dist/cli/loop-executor.d.ts.map +1 -1
- package/dist/cli/loop-executor.js +60 -13
- package/dist/cli/loop-executor.js.map +1 -1
- package/dist/cli/parity.d.ts +12 -0
- package/dist/cli/parity.d.ts.map +1 -1
- package/dist/cli/parity.js +46 -1
- package/dist/cli/parity.js.map +1 -1
- package/dist/cli/stats.d.ts +62 -4
- package/dist/cli/stats.d.ts.map +1 -1
- package/dist/cli/stats.js +282 -5
- package/dist/cli/stats.js.map +1 -1
- package/dist/cli/tool-install-prompt.d.ts +2 -7
- package/dist/cli/tool-install-prompt.d.ts.map +1 -1
- package/dist/cli/tool-install-prompt.js +11 -28
- package/dist/cli/tool-install-prompt.js.map +1 -1
- package/dist/cli/ui/index.d.ts +2 -2
- package/dist/cli/ui/index.d.ts.map +1 -1
- package/dist/cli/ui/index.js +1 -1
- package/dist/cli/ui/index.js.map +1 -1
- package/dist/cli/ui/presenter.d.ts +43 -0
- package/dist/cli/ui/presenter.d.ts.map +1 -1
- package/dist/cli/ui/presenter.js +88 -1
- package/dist/cli/ui/presenter.js.map +1 -1
- package/dist/config/manager.d.ts +29 -1
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +178 -143
- package/dist/config/manager.js.map +1 -1
- package/dist/config/paths.d.ts +48 -1
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +59 -9
- package/dist/config/paths.js.map +1 -1
- package/dist/config/scratch-workspace.d.ts +134 -0
- package/dist/config/scratch-workspace.d.ts.map +1 -0
- package/dist/config/scratch-workspace.js +213 -0
- package/dist/config/scratch-workspace.js.map +1 -0
- package/dist/config/service-catalog.js +1 -1
- package/dist/config/service-catalog.js.map +1 -1
- package/dist/config/service-probe.js +1 -1
- package/dist/config/service-probe.js.map +1 -1
- package/dist/config/types.d.ts +22 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/gateway/adapters.d.ts +24 -0
- package/dist/gateway/adapters.d.ts.map +1 -1
- package/dist/gateway/adapters.js +20 -8
- package/dist/gateway/adapters.js.map +1 -1
- package/dist/gateway/oversized-reply.d.ts +39 -0
- package/dist/gateway/oversized-reply.d.ts.map +1 -0
- package/dist/gateway/oversized-reply.js +54 -0
- package/dist/gateway/oversized-reply.js.map +1 -0
- package/dist/gateway/registry.d.ts +0 -10
- package/dist/gateway/registry.d.ts.map +1 -1
- package/dist/gateway/registry.js +149 -5
- package/dist/gateway/registry.js.map +1 -1
- package/dist/learning/auto-router.d.ts +30 -4
- package/dist/learning/auto-router.d.ts.map +1 -1
- package/dist/learning/auto-router.js +54 -38
- package/dist/learning/auto-router.js.map +1 -1
- package/dist/learning/cost-classification.d.ts +188 -0
- package/dist/learning/cost-classification.d.ts.map +1 -0
- package/dist/learning/cost-classification.js +294 -0
- package/dist/learning/cost-classification.js.map +1 -0
- package/dist/learning/cost-ledger-maintenance.d.ts +122 -0
- package/dist/learning/cost-ledger-maintenance.d.ts.map +1 -0
- package/dist/learning/cost-ledger-maintenance.js +289 -0
- package/dist/learning/cost-ledger-maintenance.js.map +1 -0
- package/dist/learning/cost-reconcile.d.ts +160 -0
- package/dist/learning/cost-reconcile.d.ts.map +1 -0
- package/dist/learning/cost-reconcile.js +238 -0
- package/dist/learning/cost-reconcile.js.map +1 -0
- package/dist/learning/cost-tracker.d.ts +134 -13
- package/dist/learning/cost-tracker.d.ts.map +1 -1
- package/dist/learning/cost-tracker.js +190 -68
- package/dist/learning/cost-tracker.js.map +1 -1
- package/dist/learning/model-pricing.d.ts +240 -0
- package/dist/learning/model-pricing.d.ts.map +1 -0
- package/dist/learning/model-pricing.js +309 -0
- package/dist/learning/model-pricing.js.map +1 -0
- package/dist/learning/model-selection.d.ts +15 -0
- package/dist/learning/model-selection.d.ts.map +1 -1
- package/dist/learning/model-selection.js +22 -2
- package/dist/learning/model-selection.js.map +1 -1
- package/dist/learning/model-warmup.d.ts.map +1 -1
- package/dist/learning/model-warmup.js +4 -1
- package/dist/learning/model-warmup.js.map +1 -1
- package/dist/learning/quota-ledger.d.ts +24 -0
- package/dist/learning/quota-ledger.d.ts.map +1 -1
- package/dist/learning/quota-ledger.js +40 -0
- package/dist/learning/quota-ledger.js.map +1 -1
- package/dist/learning/runtime-dependencies.d.ts +183 -0
- package/dist/learning/runtime-dependencies.d.ts.map +1 -0
- package/dist/learning/runtime-dependencies.js +349 -0
- package/dist/learning/runtime-dependencies.js.map +1 -0
- package/dist/learning/session-store.d.ts +1 -5
- package/dist/learning/session-store.d.ts.map +1 -1
- package/dist/learning/session-store.js +10 -1
- package/dist/learning/session-store.js.map +1 -1
- package/dist/learning/skill-store.d.ts.map +1 -1
- package/dist/learning/skill-store.js +6 -5
- package/dist/learning/skill-store.js.map +1 -1
- package/dist/learning/step-checkpoint.d.ts +32 -3
- package/dist/learning/step-checkpoint.d.ts.map +1 -1
- package/dist/learning/step-checkpoint.js +33 -4
- package/dist/learning/step-checkpoint.js.map +1 -1
- package/dist/mcp/settings.d.ts +15 -3
- package/dist/mcp/settings.d.ts.map +1 -1
- package/dist/mcp/settings.js +30 -15
- package/dist/mcp/settings.js.map +1 -1
- package/dist/parity/drivers.d.ts.map +1 -1
- package/dist/parity/drivers.js +31 -5
- package/dist/parity/drivers.js.map +1 -1
- package/dist/skills/bundled-skills-phase3.d.ts +14 -0
- package/dist/skills/bundled-skills-phase3.d.ts.map +1 -1
- package/dist/skills/bundled-skills-phase3.js +50 -0
- package/dist/skills/bundled-skills-phase3.js.map +1 -1
- package/dist/tools/child-agent-runtime.d.ts.map +1 -1
- package/dist/tools/child-agent-runtime.js +13 -7
- package/dist/tools/child-agent-runtime.js.map +1 -1
- package/dist/tools/followup-utils.d.ts +36 -0
- package/dist/tools/followup-utils.d.ts.map +1 -1
- package/dist/tools/followup-utils.js +66 -0
- package/dist/tools/followup-utils.js.map +1 -1
- package/dist/tools/modality/browser.js +2 -2
- package/dist/tools/modality/browser.js.map +1 -1
- package/dist/tools/modality/image-gen.d.ts +1 -1
- package/dist/tools/modality/image-gen.js +1 -1
- package/dist/tools/modality/image-providers.js +1 -1
- package/dist/tools/modality/image-providers.js.map +1 -1
- package/dist/tools/modality/motion-presets.d.ts +62 -0
- package/dist/tools/modality/motion-presets.d.ts.map +1 -0
- package/dist/tools/modality/motion-presets.js +399 -0
- package/dist/tools/modality/motion-presets.js.map +1 -0
- package/dist/tools/modality/motion-tool.d.ts +62 -0
- package/dist/tools/modality/motion-tool.d.ts.map +1 -0
- package/dist/tools/modality/motion-tool.js +220 -0
- package/dist/tools/modality/motion-tool.js.map +1 -0
- package/dist/tools/modality/motion.d.ts +298 -0
- package/dist/tools/modality/motion.d.ts.map +1 -0
- package/dist/tools/modality/motion.js +514 -0
- package/dist/tools/modality/motion.js.map +1 -0
- package/dist/tools/modality/vision.d.ts +1 -1
- package/dist/tools/modality/vision.js +1 -1
- package/dist/tools/modality/voice.js +2 -2
- package/dist/tools/modality/voice.js.map +1 -1
- package/dist/tools/neutts-synth.d.ts +3 -3
- package/dist/tools/neutts-synth.js +3 -3
- package/dist/tools/neutts-synth.js.map +1 -1
- package/dist/tools/process-liveness.d.ts +50 -0
- package/dist/tools/process-liveness.d.ts.map +1 -1
- package/dist/tools/process-liveness.js +82 -9
- package/dist/tools/process-liveness.js.map +1 -1
- package/dist/tools/registry.d.ts +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +102 -4
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/run-terminal.d.ts.map +1 -1
- package/dist/tools/run-terminal.js +18 -1
- package/dist/tools/run-terminal.js.map +1 -1
- package/dist/tools/tool-loop.d.ts.map +1 -1
- package/dist/tools/tool-loop.js +71 -19
- package/dist/tools/tool-loop.js.map +1 -1
- package/dist/tools/toolsets.js +2 -2
- package/dist/tools/toolsets.js.map +1 -1
- package/dist/tools/web-research.d.ts +1 -1
- package/dist/tools/web-research.js +3 -3
- package/dist/tools/web-research.js.map +1 -1
- package/dist/utils/workspace-need.d.ts +84 -0
- package/dist/utils/workspace-need.d.ts.map +1 -0
- package/dist/utils/workspace-need.js +227 -0
- package/dist/utils/workspace-need.js.map +1 -0
- package/dist/web-dashboard/chat-console.d.ts +78 -0
- package/dist/web-dashboard/chat-console.d.ts.map +1 -1
- package/dist/web-dashboard/chat-console.js +122 -12
- package/dist/web-dashboard/chat-console.js.map +1 -1
- package/dist/web-dashboard/hub-data.d.ts +9 -1
- package/dist/web-dashboard/hub-data.d.ts.map +1 -1
- package/dist/web-dashboard/hub-data.js +2 -0
- package/dist/web-dashboard/hub-data.js.map +1 -1
- package/dist/web-dashboard/project-context.d.ts +1 -1
- package/dist/web-dashboard/project-context.d.ts.map +1 -1
- package/dist/web-dashboard/project-context.js +7 -1
- package/dist/web-dashboard/project-context.js.map +1 -1
- package/dist/web-dashboard/server.d.ts +33 -0
- package/dist/web-dashboard/server.d.ts.map +1 -1
- package/dist/web-dashboard/server.js +326 -23
- package/dist/web-dashboard/server.js.map +1 -1
- package/dist/web-dashboard/src/types.d.ts +109 -0
- package/dist/web-dashboard/src/types.d.ts.map +1 -1
- package/dist/web-dashboard/task-runner.d.ts +17 -1
- package/dist/web-dashboard/task-runner.d.ts.map +1 -1
- package/dist/web-dashboard/task-runner.js +40 -4
- package/dist/web-dashboard/task-runner.js.map +1 -1
- package/dist/web-dashboard/workspace-guard.d.ts +20 -21
- package/dist/web-dashboard/workspace-guard.d.ts.map +1 -1
- package/dist/web-dashboard/workspace-guard.js +20 -153
- package/dist/web-dashboard/workspace-guard.js.map +1 -1
- package/dist/web-dashboard/workspace-resolution.d.ts +23 -5
- package/dist/web-dashboard/workspace-resolution.d.ts.map +1 -1
- package/dist/web-dashboard/workspace-resolution.js +22 -4
- package/dist/web-dashboard/workspace-resolution.js.map +1 -1
- package/package.json +1 -1
- package/src/web-dashboard/public/assets/index-CzRU84ET.css +1 -0
- package/src/web-dashboard/public/assets/index-V0rIqVbg.js +236 -0
- package/src/web-dashboard/public/assets/index-V0rIqVbg.js.map +1 -0
- package/src/web-dashboard/public/index.html +2 -2
- package/src/web-dashboard/public/assets/index-DycGBWwy.js +0 -236
- package/src/web-dashboard/public/assets/index-DycGBWwy.js.map +0 -1
- package/src/web-dashboard/public/assets/index-aO3aG7ex.css +0 -1
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* Opens at: http://localhost:3030
|
|
11
11
|
*/
|
|
12
12
|
import { createServer } from 'node:http';
|
|
13
|
+
import { type DependencyStatus } from '../learning/runtime-dependencies.js';
|
|
13
14
|
import type { WireFinding } from '../findings/verdicts.js';
|
|
14
15
|
import { WhatsAppPairingManager } from './whatsapp-pairing.js';
|
|
15
16
|
import { ChatConsole } from './chat-console.js';
|
|
@@ -304,6 +305,38 @@ export declare function readTracesData(): {
|
|
|
304
305
|
};
|
|
305
306
|
/** Full trace detail (steps included) for the replay view. */
|
|
306
307
|
export declare function readTraceDetail(id: string): DashboardTrace | null;
|
|
308
|
+
/**
|
|
309
|
+
* One runtime tool the agent uses but does not ship, as the System page's panel
|
|
310
|
+
* needs it. Mirrored by `RuntimeTool` in the dashboard's `types.ts` — the same
|
|
311
|
+
* split `AdminProviderSummary` uses, so the wire shape is stated where it is
|
|
312
|
+
* produced and where it is rendered, not imported across the boundary.
|
|
313
|
+
*
|
|
314
|
+
* The server maps the DECLARATION into this DTO rather than shipping the whole
|
|
315
|
+
* dependency object: the panel renders, it does not decide, and the internal
|
|
316
|
+
* probe list has no business on the wire.
|
|
317
|
+
*/
|
|
318
|
+
interface RuntimeTool {
|
|
319
|
+
id: string;
|
|
320
|
+
label: string;
|
|
321
|
+
need: 'recommended' | 'optional';
|
|
322
|
+
/** Whether the server found it on THIS machine. */
|
|
323
|
+
present: boolean;
|
|
324
|
+
/** What installing it buys, one sentence. */
|
|
325
|
+
enables: string;
|
|
326
|
+
/** The tools that need it (cited under the row). */
|
|
327
|
+
neededBy: string[];
|
|
328
|
+
/** The command for this OS, or null when no one-liner applies here. */
|
|
329
|
+
installCommand: string | null;
|
|
330
|
+
/** The caveat that bites, shown with the command. */
|
|
331
|
+
note?: string;
|
|
332
|
+
/** True when running it needs a human (sudo, a GUI installer). */
|
|
333
|
+
needsHumanInput: boolean;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* One runtime tool as the panel's payload. Exported for the server test, which
|
|
337
|
+
* asserts the panel is fed the same list `nuvira doctor` prints.
|
|
338
|
+
*/
|
|
339
|
+
export declare function runtimeToolSummary(status: DependencyStatus): RuntimeTool;
|
|
307
340
|
/** Test hook: swap the chat console (e.g. a fake engine) — routes read the
|
|
308
341
|
* module variable at request time, so this works anytime. The Phase 4
|
|
309
342
|
* DAG-telemetry hook is re-attached to the replacement. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web-dashboard/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAmC,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web-dashboard/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,7 +25,9 @@ import { loadEnv } from '../utils/env.js';
|
|
|
25
25
|
import { ConfigManager } from '../config/manager.js';
|
|
26
26
|
import { parseRuntimeSpec } from '../mcp/runtime.js';
|
|
27
27
|
import { runAllChecks } from '../cli/doctor.js';
|
|
28
|
+
import { dependencySummary, probeRuntimeDependencies, } from '../learning/runtime-dependencies.js';
|
|
28
29
|
import { getAutoRouter } from '../learning/auto-router.js';
|
|
30
|
+
import { classifyEntries, } from '../learning/cost-classification.js';
|
|
29
31
|
import { acceptanceSummary, collectLabelledTurns, serializeLabelledTurns, parseCorpusText, detectCorpusFormat, mergeImportedLabels, } from '../learning/acceptance-model.js';
|
|
30
32
|
import { readDecisions, recallDecisions, reviseDecision } from '../learning/decision-log.js';
|
|
31
33
|
import { degradedCallsFromTraces } from '../learning/reasoning-trace.js';
|
|
@@ -54,6 +56,7 @@ import { isCuratedProcessEnvVar, normalizeProcessEnvValue, } from '../config/pro
|
|
|
54
56
|
import { FRESH_DAYS, FRESHNESS_COPY, REACHABILITY_COPY, classifyFreshness, classifyReachability, } from '../learning/model-reachability.js';
|
|
55
57
|
import { VERIFY_BACKLOG_DEFAULT_PER_RUN, VERIFY_BACKLOG_MAX_PER_RUN, getVerifyBacklogState, startVerifyBacklogRun, } from '../learning/model-verify-job.js';
|
|
56
58
|
import { setToolsetEnabled } from '../tools/toolsets.js';
|
|
59
|
+
import { ArtifactStore } from '../tools/artifact-store.js';
|
|
57
60
|
import { setSkillEnabled } from '../learning/hub-skill-catalog.js';
|
|
58
61
|
// I11 — dashboard channel send-test: the same registry/adapters `nuvira gateway
|
|
59
62
|
// send` uses, so a Channels-tab test message behaves identically to the CLI.
|
|
@@ -67,6 +70,7 @@ import { ChannelDirectory, PLATFORM_ENV_VARS, platformConfigVars } from '../gate
|
|
|
67
70
|
import { applyEnvToProcess, configurablePlatforms, envVarState, platformConfigStatus, platformEnvVarMeta, redactValue, writeEnvFile, } from '../gateway/platform-config.js';
|
|
68
71
|
import { SERVICE_CATALOG, getServiceDefinition, } from '../config/service-catalog.js';
|
|
69
72
|
import { probeService } from '../config/service-probe.js';
|
|
73
|
+
import { pruneScratchWorkspaces } from '../config/scratch-workspace.js';
|
|
70
74
|
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
71
75
|
const PORT = parseInt(envBuff('DASHBOARD_PORT') || '3030', 10);
|
|
72
76
|
const HOST = envBuff('DASHBOARD_HOST') || '127.0.0.1';
|
|
@@ -118,6 +122,40 @@ let alwaysWatchQuota = false;
|
|
|
118
122
|
* loadApiKeysFromConfig uses). Best-effort — a missing/corrupt config just
|
|
119
123
|
* keeps the default (false = arm-on-connect only).
|
|
120
124
|
*/
|
|
125
|
+
/**
|
|
126
|
+
* The content type for an artifact file: the recorded mime when there is one,
|
|
127
|
+
* otherwise the extension. Video and image types are the ones the panel renders
|
|
128
|
+
* inline, so a wrong type is a blank frame rather than a failed download.
|
|
129
|
+
*/
|
|
130
|
+
function artifactFileMime(file, recorded) {
|
|
131
|
+
if (recorded && /^[a-z]+\/[a-z0-9.+-]+$/i.test(recorded))
|
|
132
|
+
return recorded;
|
|
133
|
+
const ext = (file.split('.').pop() ?? '').toLowerCase();
|
|
134
|
+
const byExt = {
|
|
135
|
+
mp4: 'video/mp4',
|
|
136
|
+
webm: 'video/webm',
|
|
137
|
+
mov: 'video/quicktime',
|
|
138
|
+
mkv: 'video/x-matroska',
|
|
139
|
+
gif: 'image/gif',
|
|
140
|
+
png: 'image/png',
|
|
141
|
+
jpg: 'image/jpeg',
|
|
142
|
+
jpeg: 'image/jpeg',
|
|
143
|
+
webp: 'image/webp',
|
|
144
|
+
svg: 'image/svg+xml',
|
|
145
|
+
mp3: 'audio/mpeg',
|
|
146
|
+
wav: 'audio/wav',
|
|
147
|
+
m4a: 'audio/mp4',
|
|
148
|
+
srt: 'application/x-subrip',
|
|
149
|
+
vtt: 'text/vtt',
|
|
150
|
+
json: 'application/json',
|
|
151
|
+
md: 'text/markdown',
|
|
152
|
+
txt: 'text/plain',
|
|
153
|
+
log: 'text/plain',
|
|
154
|
+
csv: 'text/csv',
|
|
155
|
+
pdf: 'application/pdf',
|
|
156
|
+
};
|
|
157
|
+
return byExt[ext] ?? 'application/octet-stream';
|
|
158
|
+
}
|
|
121
159
|
function loadAlwaysWatchQuotaFlag() {
|
|
122
160
|
try {
|
|
123
161
|
const configPath = resolveBuffConfigPath();
|
|
@@ -1513,9 +1551,52 @@ function readCostData() {
|
|
|
1513
1551
|
if (!data?.entries) {
|
|
1514
1552
|
// The empty state deliberately omits `recent` entirely (the dashboard reads
|
|
1515
1553
|
// a missing list as "nothing recorded"), matching the other zero counters.
|
|
1516
|
-
return {
|
|
1554
|
+
return {
|
|
1555
|
+
totalRequests: 0,
|
|
1556
|
+
totalCost: 0,
|
|
1557
|
+
byProvider: {},
|
|
1558
|
+
byModel: {},
|
|
1559
|
+
billedUsd: 0,
|
|
1560
|
+
billedReportedUsd: 0,
|
|
1561
|
+
billedListedUsd: 0,
|
|
1562
|
+
unverifiedListedUsd: 0,
|
|
1563
|
+
notBilledValueUsd: 0,
|
|
1564
|
+
unpricedCalls: 0,
|
|
1565
|
+
unpricedTokens: 0,
|
|
1566
|
+
recomputedCalls: 0,
|
|
1567
|
+
testOriginCalls: 0,
|
|
1568
|
+
// No invoice has been checked yet — stated, not implied.
|
|
1569
|
+
reconciliations: [],
|
|
1570
|
+
};
|
|
1517
1571
|
}
|
|
1518
1572
|
const entries = data.entries;
|
|
1573
|
+
// ── The classified view (ONE judgement for every surface) ────────────────
|
|
1574
|
+
// The Cost page's headline used to be `totalCost` — the sum of a column the
|
|
1575
|
+
// ledger had priced from its own hardcoded table — printed under the words
|
|
1576
|
+
// "Total Spent". It is now split by EVIDENCE: what was billed (provider-reported
|
|
1577
|
+
// exact + list-priced with a declared paid account), the value a free tier
|
|
1578
|
+
// absorbed, the list-priced rows whose tier is undeclared, and the calls with no
|
|
1579
|
+
// price at all. Test-origin rows are excluded here: a row written by a test run
|
|
1580
|
+
// is not the user's usage (the model registry takes the same posture).
|
|
1581
|
+
const classified = classifyEntries(entries, {
|
|
1582
|
+
excludeTestOrigin: true,
|
|
1583
|
+
});
|
|
1584
|
+
const byProviderBilled = {};
|
|
1585
|
+
const byProviderUnverified = {};
|
|
1586
|
+
const byProviderValue = {};
|
|
1587
|
+
const byProviderUnpricedCalls = {};
|
|
1588
|
+
const byModelBilled = {};
|
|
1589
|
+
const byModelValue = {};
|
|
1590
|
+
for (const [provider, t] of Object.entries(classified.byProvider)) {
|
|
1591
|
+
byProviderBilled[provider] = t.billedUsd;
|
|
1592
|
+
byProviderUnverified[provider] = t.unverifiedListedUsd;
|
|
1593
|
+
byProviderValue[provider] = t.notBilledValueUsd;
|
|
1594
|
+
byProviderUnpricedCalls[provider] = t.unpricedCalls;
|
|
1595
|
+
}
|
|
1596
|
+
for (const [model, t] of Object.entries(classified.byModel)) {
|
|
1597
|
+
byModelBilled[model] = t.billedUsd;
|
|
1598
|
+
byModelValue[model] = t.notBilledValueUsd;
|
|
1599
|
+
}
|
|
1519
1600
|
const totalCost = entries.reduce((s, e) => s + (typeof e.costUsd === 'number' ? e.costUsd : 0), 0);
|
|
1520
1601
|
const totalTokens = entries.reduce((s, e) => s + (typeof e.totalTokens === 'number' ? e.totalTokens : 0), 0);
|
|
1521
1602
|
const byProvider = {};
|
|
@@ -1567,9 +1648,53 @@ function readCostData() {
|
|
|
1567
1648
|
measured: e.measured === true,
|
|
1568
1649
|
reported: e.costReported === true,
|
|
1569
1650
|
}));
|
|
1651
|
+
// ── Invoice checks: the only EXTERNAL figures on this page ───────────────
|
|
1652
|
+
// Written by `nuvira stats cost reconcile` when the operator enters the
|
|
1653
|
+
// provider's own charge for a window. Everything else here is an estimate of
|
|
1654
|
+
// money owed; this is the one place the page can be compared against what was
|
|
1655
|
+
// ACTUALLY charged. Read straight from the file (no module import) so the
|
|
1656
|
+
// dashboard keeps resolving its own memory dir, and capped like `recent`.
|
|
1657
|
+
const reconciliationFile = readJSON(join(MEMORY_DIR, 'cost-reconciliations.json'));
|
|
1658
|
+
const reconciliations = (Array.isArray(reconciliationFile?.reconciliations) ? reconciliationFile.reconciliations : [])
|
|
1659
|
+
.filter((r) => r && typeof r.provider === 'string' && typeof r.invoiceUsd === 'number')
|
|
1660
|
+
.slice(0, 20)
|
|
1661
|
+
.map((r) => ({
|
|
1662
|
+
provider: r.provider,
|
|
1663
|
+
invoiceUsd: r.invoiceUsd,
|
|
1664
|
+
billedUsd: r.billedUsd,
|
|
1665
|
+
deltaUsd: r.deltaUsd,
|
|
1666
|
+
verdict: r.verdict,
|
|
1667
|
+
since: r.since,
|
|
1668
|
+
until: r.until,
|
|
1669
|
+
recordedAt: r.recordedAt,
|
|
1670
|
+
...(r.note ? { note: r.note } : {}),
|
|
1671
|
+
...(r.tierEvidence ? { tier: r.tierEvidence.tier } : {}),
|
|
1672
|
+
}));
|
|
1570
1673
|
return {
|
|
1571
1674
|
totalRequests: entries.length,
|
|
1572
1675
|
totalCost: Math.round(totalCost * 100000) / 100000,
|
|
1676
|
+
// The evidence-backed figures. `totalCost` above is kept for compatibility
|
|
1677
|
+
// (it is the ledger total as recorded, including rows priced by the retired
|
|
1678
|
+
// hardcoded table); these are what the page headlines.
|
|
1679
|
+
billedUsd: classified.billedUsd,
|
|
1680
|
+
billedReportedUsd: classified.billedReportedUsd,
|
|
1681
|
+
billedListedUsd: classified.billedListedUsd,
|
|
1682
|
+
billedReportedCalls: classified.billedReportedCalls,
|
|
1683
|
+
billedListedCalls: classified.billedListedCalls,
|
|
1684
|
+
unverifiedListedUsd: classified.unverifiedListedUsd,
|
|
1685
|
+
unverifiedListedCalls: classified.unverifiedListedCalls,
|
|
1686
|
+
notBilledValueUsd: classified.notBilledValueUsd,
|
|
1687
|
+
notBilledValueCalls: classified.notBilledValueCalls,
|
|
1688
|
+
unpricedCalls: classified.unpricedCalls,
|
|
1689
|
+
unpricedTokens: classified.unpricedTokens,
|
|
1690
|
+
recomputedCalls: classified.recomputedCalls,
|
|
1691
|
+
testOriginCalls: classified.testOriginCalls,
|
|
1692
|
+
byProviderBilled,
|
|
1693
|
+
byProviderUnverified,
|
|
1694
|
+
byProviderValue,
|
|
1695
|
+
byProviderUnpricedCalls,
|
|
1696
|
+
byModelBilled,
|
|
1697
|
+
byModelValue,
|
|
1573
1698
|
totalTokens,
|
|
1574
1699
|
byProvider,
|
|
1575
1700
|
byModel,
|
|
@@ -1581,6 +1706,7 @@ function readCostData() {
|
|
|
1581
1706
|
estimatedCost: Math.round(estimatedCost * 100000) / 100000,
|
|
1582
1707
|
reportedCalls,
|
|
1583
1708
|
reportedCost: Math.round(reportedCost * 100000) / 100000,
|
|
1709
|
+
reconciliations,
|
|
1584
1710
|
recent,
|
|
1585
1711
|
};
|
|
1586
1712
|
}
|
|
@@ -2718,14 +2844,42 @@ function buildServiceRow(def, canReadValues) {
|
|
|
2718
2844
|
envVars,
|
|
2719
2845
|
};
|
|
2720
2846
|
}
|
|
2847
|
+
/**
|
|
2848
|
+
* One runtime tool as the panel's payload. Exported for the server test, which
|
|
2849
|
+
* asserts the panel is fed the same list `nuvira doctor` prints.
|
|
2850
|
+
*/
|
|
2851
|
+
export function runtimeToolSummary(status) {
|
|
2852
|
+
return {
|
|
2853
|
+
id: status.dependency.id,
|
|
2854
|
+
label: status.dependency.label,
|
|
2855
|
+
need: status.dependency.need,
|
|
2856
|
+
present: status.present,
|
|
2857
|
+
enables: status.dependency.enables,
|
|
2858
|
+
neededBy: status.dependency.neededBy,
|
|
2859
|
+
// A tool the machine ALREADY has gets no command on the wire, the same way
|
|
2860
|
+
// `nuvira doctor` prints a fix only for what is missing: sending one anyway
|
|
2861
|
+
// invites a user to reinstall something that is working.
|
|
2862
|
+
installCommand: status.present ? null : status.installCommand,
|
|
2863
|
+
note: status.dependency.note,
|
|
2864
|
+
needsHumanInput: status.needsHumanInput,
|
|
2865
|
+
};
|
|
2866
|
+
}
|
|
2721
2867
|
/** Run ALL state checks — the dashboard command-runner payload. */
|
|
2722
2868
|
async function runAdminChecks() {
|
|
2723
2869
|
const configManager = new ConfigManager();
|
|
2724
2870
|
const { system, enterprise } = await runAllChecks(configManager);
|
|
2871
|
+
// The SAME declaration the CLI's `nuvira doctor`, the `--fix` installer and the
|
|
2872
|
+
// setup scripts read, probed once for this payload. The rows are also in
|
|
2873
|
+
// `system` (one check each) because a pass/warn verdict is what a check list
|
|
2874
|
+
// is for; this is the actionable half — the command to copy, the
|
|
2875
|
+
// recommended/optional split, and the caveat.
|
|
2876
|
+
const statuses = probeRuntimeDependencies();
|
|
2725
2877
|
return {
|
|
2726
2878
|
system,
|
|
2727
2879
|
enterprise,
|
|
2728
2880
|
providers: buildAdminProviderSummary(configManager),
|
|
2881
|
+
runtimeTools: statuses.map(runtimeToolSummary),
|
|
2882
|
+
runtimeToolsSummary: dependencySummary(statuses),
|
|
2729
2883
|
serverTime: Date.now(),
|
|
2730
2884
|
};
|
|
2731
2885
|
}
|
|
@@ -3223,6 +3377,52 @@ function handleRequest(req, res) {
|
|
|
3223
3377
|
})();
|
|
3224
3378
|
return;
|
|
3225
3379
|
}
|
|
3380
|
+
// ── An artifact's BYTES — how the Artifacts tab shows a rendered video rather
|
|
3381
|
+
// than a filename. The path is looked up in OUR OWN artifact store by (session,
|
|
3382
|
+
// artifact id) and is never taken from the query string, so a caller cannot ask
|
|
3383
|
+
// this endpoint for an arbitrary file; a bad pair is a 404, not a read.
|
|
3384
|
+
if (pathname === '/api/artifact-file') {
|
|
3385
|
+
const sessionId = url.searchParams.get('session') ?? '';
|
|
3386
|
+
const artifactId = url.searchParams.get('artifact') ?? '';
|
|
3387
|
+
const artifact = artifactId
|
|
3388
|
+
? new ArtifactStore().read(sessionId).find((a) => a.id === artifactId)
|
|
3389
|
+
: undefined;
|
|
3390
|
+
if (!artifact || !artifact.path || !existsSync(artifact.path) || !statSync(artifact.path).isFile()) {
|
|
3391
|
+
writeJson(res, 404, { ok: false, error: 'No such artifact file.' });
|
|
3392
|
+
return;
|
|
3393
|
+
}
|
|
3394
|
+
const size = statSync(artifact.path).size;
|
|
3395
|
+
const type = artifactFileMime(artifact.path, artifact.mime);
|
|
3396
|
+
// Range support, because a <video> that cannot seek cannot be reviewed: a
|
|
3397
|
+
// player asks for byte ranges the moment the user drags the scrubber.
|
|
3398
|
+
const range = /^bytes=(\d*)-(\d*)$/.exec(req.headers.range ?? '');
|
|
3399
|
+
if (range) {
|
|
3400
|
+
const start = range[1] ? Number(range[1]) : 0;
|
|
3401
|
+
const end = range[2] ? Math.min(Number(range[2]), size - 1) : size - 1;
|
|
3402
|
+
if (start >= size || end < start) {
|
|
3403
|
+
res.writeHead(416, { 'Content-Range': `bytes */${size}` });
|
|
3404
|
+
res.end();
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
res.writeHead(206, {
|
|
3408
|
+
'Content-Type': type,
|
|
3409
|
+
'Content-Length': String(end - start + 1),
|
|
3410
|
+
'Content-Range': `bytes ${start}-${end}/${size}`,
|
|
3411
|
+
'Accept-Ranges': 'bytes',
|
|
3412
|
+
'Cache-Control': 'no-store',
|
|
3413
|
+
});
|
|
3414
|
+
createReadStream(artifact.path, { start, end }).pipe(res);
|
|
3415
|
+
return;
|
|
3416
|
+
}
|
|
3417
|
+
res.writeHead(200, {
|
|
3418
|
+
'Content-Type': type,
|
|
3419
|
+
'Content-Length': String(size),
|
|
3420
|
+
'Accept-Ranges': 'bytes',
|
|
3421
|
+
'Cache-Control': 'no-store',
|
|
3422
|
+
});
|
|
3423
|
+
createReadStream(artifact.path).pipe(res);
|
|
3424
|
+
return;
|
|
3425
|
+
}
|
|
3226
3426
|
if (pathname === '/api/benchmarks') {
|
|
3227
3427
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3228
3428
|
res.end(JSON.stringify(readBenchmarkData()));
|
|
@@ -3302,7 +3502,17 @@ function handleRequest(req, res) {
|
|
|
3302
3502
|
res.end(JSON.stringify(payload));
|
|
3303
3503
|
}).catch((err) => {
|
|
3304
3504
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
3305
|
-
res.end(JSON.stringify({
|
|
3505
|
+
res.end(JSON.stringify({
|
|
3506
|
+
error: String(err),
|
|
3507
|
+
system: [],
|
|
3508
|
+
enterprise: [],
|
|
3509
|
+
providers: [],
|
|
3510
|
+
// An empty list, not a missing one: the panel must be able to say
|
|
3511
|
+
// "nothing reported" on a failed run rather than crash on undefined.
|
|
3512
|
+
runtimeTools: [],
|
|
3513
|
+
runtimeToolsSummary: 'unavailable — the checks run failed',
|
|
3514
|
+
serverTime: Date.now(),
|
|
3515
|
+
}));
|
|
3306
3516
|
});
|
|
3307
3517
|
return;
|
|
3308
3518
|
}
|
|
@@ -7216,7 +7426,27 @@ function handleRequest(req, res) {
|
|
|
7216
7426
|
// `busy` lets a client that reloaded mid-turn know the answer is still being
|
|
7217
7427
|
// produced, so it can keep the bubble alive and poll instead of showing a
|
|
7218
7428
|
// finished-looking thread.
|
|
7219
|
-
|
|
7429
|
+
// `pendingQuestion` rides with the transcript because a reload has to be
|
|
7430
|
+
// able to SEE an open question, not just guess at it from `busy`. The turn
|
|
7431
|
+
// holding for an answer reports `busy: true` exactly like one that is
|
|
7432
|
+
// working, so without this the resumed page showed "⏳ Still working on the
|
|
7433
|
+
// server" for a turn that was waiting on the user (measured 2026-10-14:
|
|
7434
|
+
// trace-1791996357488-wpm352 sat in `ask_user` for 5m02s and was cancelled
|
|
7435
|
+
// with the question never on screen).
|
|
7436
|
+
// `busySince` is when the RUNNING turn started, so a client that gave up on
|
|
7437
|
+
// its own POST (a 5-minute ceiling) or reloaded mid-turn reports how long
|
|
7438
|
+
// the wait really is rather than counting from whenever it attached
|
|
7439
|
+
// (measured 2026-10-14: a 664s turn shown as "1m 25s elapsed").
|
|
7440
|
+
writeJson(res, 200, {
|
|
7441
|
+
ok: true,
|
|
7442
|
+
session: {
|
|
7443
|
+
id,
|
|
7444
|
+
...rec,
|
|
7445
|
+
busy: chatConsole.isBusy(id),
|
|
7446
|
+
...(chatConsole.busySince(id) !== null ? { busySince: chatConsole.busySince(id) } : {}),
|
|
7447
|
+
...(chatConsole.pendingQuestionFor(id) ? { pendingQuestion: chatConsole.pendingQuestionFor(id) } : {}),
|
|
7448
|
+
},
|
|
7449
|
+
});
|
|
7220
7450
|
return;
|
|
7221
7451
|
}
|
|
7222
7452
|
// GET /api/continuity — the stored session snapshots + the semantic recall
|
|
@@ -7489,13 +7719,51 @@ function handleRequest(req, res) {
|
|
|
7489
7719
|
*/
|
|
7490
7720
|
const configuredCwd = resolveDashboardCwd();
|
|
7491
7721
|
const { resolveTurnWorkspace, formatWorkspaceNoticeText } = await import('./workspace-resolution.js');
|
|
7722
|
+
const { workspaceNeedForMissingFolder } = await import('./workspace-guard.js');
|
|
7492
7723
|
const sessionProjectPath = chatConsole.get(sessionId)?.projectPath;
|
|
7493
|
-
|
|
7724
|
+
/**
|
|
7725
|
+
* The same ladder, asked twice when the last rung is needed.
|
|
7726
|
+
*
|
|
7727
|
+
* WHY TWO PASSES. The scratch folder is a real directory on disk, so it is
|
|
7728
|
+
* only created when the ladder has actually RUN OUT (nothing attached,
|
|
7729
|
+
* nothing named in the message, no configured default) AND the ask needs
|
|
7730
|
+
* somewhere to PUT new work. Creating it eagerly would leave an empty
|
|
7731
|
+
* temporary folder behind on every turn that already had a folder, and
|
|
7732
|
+
* handing it to a READ ask would be the kuttaaddon defect again — an empty
|
|
7733
|
+
* folder cannot answer "assess this project".
|
|
7734
|
+
*/
|
|
7735
|
+
const resolveWorkspaceNow = (scratchPath) => resolveTurnWorkspace({
|
|
7494
7736
|
...(attachedPath ? { attachedPath } : {}),
|
|
7495
7737
|
...(sessionProjectPath ? { sessionPath: sessionProjectPath } : {}),
|
|
7496
7738
|
...(configuredCwd ? { configuredCwd } : {}),
|
|
7497
7739
|
message,
|
|
7740
|
+
...(scratchPath ? { scratchPath } : {}),
|
|
7498
7741
|
});
|
|
7742
|
+
let workspace = resolveWorkspaceNow();
|
|
7743
|
+
/**
|
|
7744
|
+
* What this ask needs a folder FOR — `'produce'` is the case a temporary
|
|
7745
|
+
* folder can serve, `'read'` is the case it cannot (and keeps the folder
|
|
7746
|
+
* prompt), `null` is plain chat, which needs none.
|
|
7747
|
+
*/
|
|
7748
|
+
const missingWorkspaceNeed = workspace.path ? null : workspaceNeedForMissingFolder(message);
|
|
7749
|
+
if (missingWorkspaceNeed === 'produce') {
|
|
7750
|
+
// The scratch folder is offered through the SAME rule the gateway uses
|
|
7751
|
+
// (`scratchWorkspaceForTurn`), so the two surfaces cannot disagree about
|
|
7752
|
+
// when a temporary folder is the right answer — in particular about an
|
|
7753
|
+
// ISOLATED turn, which a scratch folder can never serve: it has no base
|
|
7754
|
+
// commit to diff against. There, this turn has no folder at all, so it
|
|
7755
|
+
// falls through to the folder request below rather than isolating a
|
|
7756
|
+
// directory the user never chose.
|
|
7757
|
+
const { scratchWorkspaceForTurn } = await import('../config/scratch-workspace.js');
|
|
7758
|
+
const { resolveIsolationRequest } = await import('../tools/worktree.js');
|
|
7759
|
+
const scratch = scratchWorkspaceForTurn({
|
|
7760
|
+
key: sessionId,
|
|
7761
|
+
need: missingWorkspaceNeed,
|
|
7762
|
+
isolationAsked: resolveIsolationRequest({ worktree }).asked,
|
|
7763
|
+
});
|
|
7764
|
+
if (scratch)
|
|
7765
|
+
workspace = resolveWorkspaceNow(scratch);
|
|
7766
|
+
}
|
|
7499
7767
|
if (workspace.path) {
|
|
7500
7768
|
projectPath = workspace.path;
|
|
7501
7769
|
const bundle = getProjectBundle(workspace.path);
|
|
@@ -7513,24 +7781,23 @@ function handleRequest(req, res) {
|
|
|
7513
7781
|
* needs no caption.
|
|
7514
7782
|
*/
|
|
7515
7783
|
const workspaceNotice = formatWorkspaceNoticeText(workspace);
|
|
7516
|
-
//
|
|
7517
|
-
//
|
|
7518
|
-
//
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
}
|
|
7784
|
+
// Still no workspace: either the ask must READ or CHANGE work that already
|
|
7785
|
+
// exists (a temporary folder holds none of it), or the scratch folder could
|
|
7786
|
+
// not be created. Both are the case for a folder request rather than an
|
|
7787
|
+
// answer about the wrong tree. General chat — `null` — is unaffected.
|
|
7788
|
+
if (!workspace.path && missingWorkspaceNeed !== null) {
|
|
7789
|
+
const { projectAttachmentPrompt, projectAttachmentFollowups } = await import('./workspace-guard.js');
|
|
7790
|
+
writeJson(res, 200, {
|
|
7791
|
+
ok: true,
|
|
7792
|
+
sessionId,
|
|
7793
|
+
content: projectAttachmentPrompt(),
|
|
7794
|
+
followups: projectAttachmentFollowups(),
|
|
7795
|
+
provider: null,
|
|
7796
|
+
model: null,
|
|
7797
|
+
generationFailed: false,
|
|
7798
|
+
needsProject: true,
|
|
7799
|
+
});
|
|
7800
|
+
return;
|
|
7534
7801
|
}
|
|
7535
7802
|
// P4 (revised) — a client DISCONNECT no longer cancels the turn. It used
|
|
7536
7803
|
// to: the server treated `res` close as an abort, which meant a page
|
|
@@ -7568,6 +7835,14 @@ function handleRequest(req, res) {
|
|
|
7568
7835
|
const result = await withStrictModel(strict, () => chatConsole.answer(sessionId, message, {
|
|
7569
7836
|
provider,
|
|
7570
7837
|
model,
|
|
7838
|
+
// P8b — the STRICT half of the pin rides into the console so the
|
|
7839
|
+
// CONVERSATION can remember it. `withStrictModel` above scopes it to
|
|
7840
|
+
// this turn, but a conversation whose lock is forgotten between turns
|
|
7841
|
+
// is a pin the user cannot actually rely on: measured live
|
|
7842
|
+
// (trace-1792068695325-42wb1j) the pin survived a reload and the lock
|
|
7843
|
+
// did not, so the engine handed the turn to another model with the
|
|
7844
|
+
// user's "this model only" instruction never in force.
|
|
7845
|
+
strict,
|
|
7571
7846
|
projectContext,
|
|
7572
7847
|
projectPath,
|
|
7573
7848
|
// Cluster G — carry the "no folder attached" state into the turn so a
|
|
@@ -7636,7 +7911,12 @@ function handleRequest(req, res) {
|
|
|
7636
7911
|
routingNotice = `Auto routing took over: this turn ran on ${served} because your pinned ${requested} was not available. To work with ${requested} only, enable strict model mode.`;
|
|
7637
7912
|
}
|
|
7638
7913
|
else if (result.generationFailed === true) {
|
|
7639
|
-
|
|
7914
|
+
// The turn ran on the pair the user pinned and stopped there. Saying
|
|
7915
|
+
// "auto routing may hand the work to another model" described the old
|
|
7916
|
+
// behaviour (a pinned turn followed the health ranking the moment its
|
|
7917
|
+
// pair returned an error) and would now be a lie: a pin waits on itself
|
|
7918
|
+
// and then asks. See `pinnedEscalation`.
|
|
7919
|
+
routingNotice = `Your pinned ${requested} did not answer, and nothing was sent to another model — the turn stopped instead of substituting. Retry it, or switch models.`;
|
|
7640
7920
|
}
|
|
7641
7921
|
}
|
|
7642
7922
|
// Did the offer we are about to send get a mechanism behind it? The client
|
|
@@ -7949,6 +8229,17 @@ function handleRequest(req, res) {
|
|
|
7949
8229
|
'X-Accel-Buffering': 'no',
|
|
7950
8230
|
});
|
|
7951
8231
|
res.write(`event: init\ndata: ${JSON.stringify({ sessionId })}\n\n`);
|
|
8232
|
+
// A question asked BEFORE this client attached is still waiting for an
|
|
8233
|
+
// answer, and the turn will hold until one arrives. Replaying it here is
|
|
8234
|
+
// what makes a reload (or a second tab) able to ANSWER it: without this the
|
|
8235
|
+
// new page shows a "still working" notice and no choice card at all, while
|
|
8236
|
+
// the turn waits on a reply the page has no way to send — the exact
|
|
8237
|
+
// "the model asked something but nothing appeared on screen" report
|
|
8238
|
+
// (2026-10-14, trace-1791996357488-wpm352, 5m02s in `ask_user`).
|
|
8239
|
+
const pendingQuestion = chatConsole.pendingQuestionFor(sessionId);
|
|
8240
|
+
if (pendingQuestion) {
|
|
8241
|
+
res.write(`event: question\ndata: ${JSON.stringify(pendingQuestion)}\n\n`);
|
|
8242
|
+
}
|
|
7952
8243
|
const heartbeat = setInterval(() => {
|
|
7953
8244
|
try {
|
|
7954
8245
|
res.write(': heartbeat\n\n');
|
|
@@ -8228,6 +8519,18 @@ export function createDashboardServer(opts) {
|
|
|
8228
8519
|
handle.ipv6Twin = undefined;
|
|
8229
8520
|
}
|
|
8230
8521
|
}
|
|
8522
|
+
// Scratch workspaces are temporary BY DEFINITION, so the ones nobody came back
|
|
8523
|
+
// to are cleaned on boot rather than accumulating in the user's home for the
|
|
8524
|
+
// life of the install (see `pruneScratchWorkspaces`). Best-effort: a failed
|
|
8525
|
+
// prune must never stop the server from coming up.
|
|
8526
|
+
try {
|
|
8527
|
+
const removed = pruneScratchWorkspaces();
|
|
8528
|
+
if (removed > 0)
|
|
8529
|
+
console.log(` 🧹 Removed ${removed} expired scratch workspace(s)`);
|
|
8530
|
+
}
|
|
8531
|
+
catch {
|
|
8532
|
+
/* best-effort */
|
|
8533
|
+
}
|
|
8231
8534
|
server.listen(bindPort, bindHost, () => {
|
|
8232
8535
|
console.log(`\n 🌐 Agent-Nuvira Dashboard`);
|
|
8233
8536
|
console.log(` ─────────────────────────`);
|