@barivia/barsom-mcp 0.23.5 → 0.23.8

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 CHANGED
@@ -32,7 +32,7 @@ MCP clients typically run it with **`npx`** (downloads on first use):
32
32
  1. **Get an API key** — Sign up at [barivia.se/dashboard](https://barivia.se/dashboard) (self-serve) or ask your admin for a `bv_…` key.
33
33
  2. **Add the MCP server** — Paste the JSON block above into your client’s MCP config (`mcp.json` in Cursor). Set `BARIVIA_API_KEY` and keep `BARIVIA_API_URL=https://api.barivia.se`.
34
34
  3. **Orient** — In chat, invoke **`guide_barsom_workflow`** (no parameters). It returns your plan-scoped tool map and async rules.
35
- 4. **Upload data** — `datasets(action=upload, file_path="your.csv")` → note `dataset_id` (and `stage_job_id` if staging is enqueued; poll until complete).
35
+ 4. **Upload data** — `datasets(action=upload, file_path="your.csv")` → note `dataset_id`. The proxy waits for staging on upload; confirm with `datasets(action=get)` `train_ready=yes` before train if you bypass the proxy wait.
36
36
  5. **Train** — `train(action=map, dataset_id=…, preset=quick)` → returns `job_id`. Poll with `jobs(action=status, job_id=…)` every 60–120s, or use **`training_monitor(job_id=…)`** for live curves.
37
37
  6. **Results** — When status is `completed`, `results(action=get, job_id=…)` for metrics + headline figure; **`results_explorer(job_id=…)`** to browse all figures.
38
38
 
@@ -53,10 +53,11 @@ MCP clients typically run it with **`npx`** (downloads on first use):
53
53
  | `BARIVIA_API_KEY` | Yes | -- | API key (starts with `bv_`) |
54
54
  | `BARIVIA_API_URL` | No | `https://api.barivia.se` | API base URL |
55
55
  | `BARIVIA_WORKSPACE_ROOT` | No | `process.cwd()` or `PWD` | Directory for relative `file_path` and `save_to_disk`. In Cursor MCP, `process.cwd()` is often the MCP install dir — add `BARIVIA_WORKSPACE_ROOT` to your MCP config `env` with your project path (e.g. `/home/user/myproject`). Absolute paths and `file://` URIs work without it. |
56
+ | `BARIVIA_WORKSPACE_EXTRA_ROOTS` | No | (empty) | Comma- or colon-separated absolute directories whose **realpath** targets are also allowed for uploads when the sandbox is on. Use for shared fixture trees reached via symlinks under the case workspace (e.g. `…/barivia-testing/data`). Does not weaken the default sandbox for paths outside these roots. |
56
57
  | `BARIVIA_FETCH_TIMEOUT_MS` | No | `60000` | Per-request HTTP timeout (ms) to the API. Increase (e.g. `120000`) on slow networks or when the API does long-running work. Large dataset uploads use a separate longer timeout internally, and `datasets(analyze)` runs asynchronously (auto-polled) so it is not bound by this. A timeout is not auto-retried (re-firing slow work would only multiply load / risk duplicates). |
57
58
  | `BARIVIA_VIZ_PORT` | No | OS-assigned | Local viz server on `127.0.0.1`. Port is **per MCP session** unless pinned — standalone `…/viz/…` URLs go stale after proxy restart. Set a fixed port for stable bookmark URLs. Diagnostic: `GET http://127.0.0.1:<port>/api/health?job_id=<id>`. |
58
59
  | `BARIVIA_UI_DELIVERY` | No | `auto` | Client-capability override for App tools: `auto` \| `apps` \| `localhost` \| `inline`. Tool responses always lead with a standalone localhost URL (hosts may list `ui://` without mounting widgets) and include a structured `ui_delivery` block. `account(status)` reports detected Apps support, viz port, and active tier. |
59
- | `BARIVIA_ENFORCE_WORKSPACE_SANDBOX` | No | `1` (enabled) | File uploads are constrained to the MCP workspace root by default. Set to `0` or `false` to allow absolute paths anywhere on the machine (high trust). **Changed in v0.x:** previously defaulted to off; now on for security. If uploads fail with "paths outside the workspace are disabled", either set `BARIVIA_WORKSPACE_ROOT` to cover your data directory, or opt out with `BARIVIA_ENFORCE_WORKSPACE_SANDBOX=0`. |
60
+ | `BARIVIA_ENFORCE_WORKSPACE_SANDBOX` | No | `1` (enabled) | File uploads are constrained to the MCP workspace root (and optional `BARIVIA_WORKSPACE_EXTRA_ROOTS`) by default. Set to `0` or `false` to allow absolute paths anywhere on the machine (high trust). If uploads fail with "symlink resolves outside workspace", either point `BARIVIA_WORKSPACE_ROOT` / `BARIVIA_WORKSPACE_EXTRA_ROOTS` at the real data dir, copy the file into the workspace, or opt out with `BARIVIA_ENFORCE_WORKSPACE_SANDBOX=0`. |
60
61
 
61
62
  Legacy `BARSOM_API_KEY` / `BARSOM_API_URL` / `BARSOM_WORKSPACE_ROOT` are also accepted as fallbacks.
62
63
 
@@ -102,7 +103,7 @@ Call at the **start of mapping work** (or when the user asks what the MCP can do
102
103
  | `preview` | Before training — inspect columns, stats, cyclic/datetime hints |
103
104
  | `analyze` | Pre-training recommendations (correlation, columns to consider dropping, etc.) |
104
105
  | `list` | Finding dataset IDs |
105
- | `get` | One dataset by id — status, staging fields, `stage_job_id`, ingest errors |
106
+ | `get` | One dataset by id — status, **`train_ready`**, staging fields, `stage_job_id`, ingest errors |
106
107
  | `subset` | Creating a filtered/sliced copy (row_range, filter, `sample_n`) — **only when the user asks**; agents must not auto-downsample before train/analyze |
107
108
  | `add_expression` | Add a derived column from an expression (formula → new column on the dataset) |
108
109
  | `reduce_spectral` | Pre-training reducer for long ordered numeric blocks (spectra, time series, sensor fingerprints, gene panels). Methods: **pca** (top-k principal components), **log_sample** (k columns at log-spaced indices — scattering, audio bands), **uniform_sample** (k columns at evenly-spaced indices — regularly-sampled time series), **stats** (6 fixed per-row summary columns). All produce one feature vector per row; appends derived columns to the dataset. |
@@ -34,14 +34,30 @@ function tailOrderingErrors(data, n = 4) {
34
34
  export function snapshotFromJob(data, elapsedSec, note) {
35
35
  const status = String(data.status ?? "unknown");
36
36
  const progress_pct = (data.progress ?? 0) * 100;
37
+ const kernelComplete = data.kernel_complete === true;
38
+ const awaitingFinalize = kernelComplete &&
39
+ status !== "completed" &&
40
+ status !== "failed" &&
41
+ status !== "cancelled";
37
42
  const snap = {
38
43
  elapsed_sec: elapsedSec,
39
44
  status,
40
- progress_pct: Math.round(progress_pct * 10) / 10,
45
+ // Kernel progress can hit 100% before finalize; do not present as job-done.
46
+ progress_pct: awaitingFinalize
47
+ ? Math.min(Math.round(progress_pct * 10) / 10, 99.0)
48
+ : Math.round(progress_pct * 10) / 10,
41
49
  };
42
- const phase = str(data, "progress_phase");
43
- if (phase)
50
+ const phaseRaw = str(data, "progress_phase");
51
+ const phase = phaseRaw && phaseRaw !== "missing" ? phaseRaw : undefined;
52
+ if (awaitingFinalize) {
53
+ snap.phase = phase && phase !== "complete" ? `${phase}+awaiting_finalize` : "awaiting_finalize";
54
+ }
55
+ else if (phase) {
44
56
  snap.phase = phase;
57
+ }
58
+ else if (str(data, "prepare_job_id") && String(data.status ?? "") !== "completed") {
59
+ snap.phase = "waiting_for_prepare";
60
+ }
45
61
  const epoch = num(data, "epoch");
46
62
  const total = num(data, "total_epochs");
47
63
  if (epoch != null)
@@ -57,7 +73,7 @@ export function snapshotFromJob(data, elapsedSec, note) {
57
73
  const panelTe = num(data, "panel_topographic_error");
58
74
  if (panelTe != null)
59
75
  snap.panel_te = Math.round(panelTe * 10_000) / 10_000;
60
- if (data.kernel_complete === true)
76
+ if (kernelComplete)
61
77
  snap.kernel_complete = true;
62
78
  const failureStage = str(data, "failure_stage");
63
79
  if (failureStage)
@@ -68,8 +84,12 @@ export function snapshotFromJob(data, elapsedSec, note) {
68
84
  const tail = tailOrderingErrors(data);
69
85
  if (tail && tail.length > 0)
70
86
  snap.ordering_errors_tail = tail;
71
- if (note)
87
+ if (note) {
72
88
  snap.note = note;
89
+ }
90
+ else if (awaitingFinalize) {
91
+ snap.note = "kernel complete — awaiting finalize (not fully done)";
92
+ }
73
93
  return snap;
74
94
  }
75
95
  /** True when a new snapshot is worth recording (phase/epoch/progress/status change). */
@@ -95,7 +115,10 @@ export function shouldRecordSnapshot(prev, next) {
95
115
  return false;
96
116
  }
97
117
  export function formatSnapshotLine(s) {
98
- const parts = [`[+${s.elapsed_sec}s] ${s.status} ${s.progress_pct.toFixed(1)}%`];
118
+ const progressLabel = s.kernel_complete && s.status !== "completed" && s.status !== "failed" && s.status !== "cancelled"
119
+ ? `kernel ${s.progress_pct.toFixed(1)}% (awaiting finalize)`
120
+ : `${s.progress_pct.toFixed(1)}%`;
121
+ const parts = [`[+${s.elapsed_sec}s] ${s.status} ${progressLabel}`];
99
122
  if (s.phase)
100
123
  parts.push(`phase ${s.phase}`);
101
124
  if (s.epoch != null && s.total_epochs != null)
@@ -106,8 +129,9 @@ export function formatSnapshotLine(s) {
106
129
  parts.push(`Epoch TE ${s.te.toFixed(4)}`);
107
130
  if (s.panel_te != null)
108
131
  parts.push(`Panel TE ${s.panel_te.toFixed(4)}`);
109
- if (s.kernel_complete)
132
+ if (s.kernel_complete && (s.status === "completed" || s.status === "failed" || s.status === "cancelled")) {
110
133
  parts.push("kernel complete");
134
+ }
111
135
  if (s.failure_stage)
112
136
  parts.push(`failure_stage ${s.failure_stage}`);
113
137
  if (s.eta_sec != null)
@@ -36,22 +36,41 @@ export function formatJobStatusText(job_id, data) {
36
36
  const progress = (data.progress ?? 0) * 100;
37
37
  const label = data.label != null && data.label !== "" ? String(data.label) : null;
38
38
  const jobDesc = label ? `Job ${label} (id: ${job_id})` : `Job ${job_id}`;
39
- const parts = [`${jobDesc}: ${status} (${progress.toFixed(1)}%)`];
39
+ const kernelComplete = data.kernel_complete === true;
40
+ const awaitingFinalize = kernelComplete && status !== "completed" && status !== "failed" && status !== "cancelled";
41
+ const progressBit = awaitingFinalize
42
+ ? `kernel ${Math.min(progress, 99).toFixed(1)}% — awaiting finalize`
43
+ : `${progress.toFixed(1)}%`;
44
+ const parts = [`${jobDesc}: ${status} (${progressBit})`];
40
45
  const attempt = data.attempt != null ? Number(data.attempt) : null;
41
46
  if (attempt != null && attempt > 1) {
42
47
  parts.push(`attempt ${attempt} (job was requeued after worker timeout — not stuck from scratch)`);
43
48
  }
44
- const phase = data.progress_phase != null && String(data.progress_phase) !== ""
49
+ const rawPhase = data.progress_phase != null && String(data.progress_phase) !== ""
45
50
  ? String(data.progress_phase)
46
51
  : null;
47
- if (status === "running") {
52
+ // API historically serialized SQL NULL as the literal "missing" (Julia missing).
53
+ const phase = rawPhase && rawPhase !== "missing" ? rawPhase : null;
54
+ const prepareJobId = data.prepare_job_id != null && String(data.prepare_job_id) !== ""
55
+ ? String(data.prepare_job_id)
56
+ : null;
57
+ if (status === "pending" && prepareJobId) {
58
+ parts.push(`phase: waiting_for_prepare (prepare_job_id=${prepareJobId})`);
59
+ }
60
+ else if (status === "running") {
48
61
  if (phase) {
49
62
  parts.push(`phase: ${phase}`);
50
63
  }
64
+ else if (prepareJobId) {
65
+ parts.push(`phase: waiting_for_prepare (prepare_job_id=${prepareJobId})`);
66
+ }
51
67
  else {
52
68
  parts.push(`phase: preprocessing (not set yet — often download/parse before first progress tick)`);
53
69
  }
54
70
  }
71
+ else if (prepareJobId && (status === "completed" || status === "failed")) {
72
+ parts.push(`prepare_job_id: ${prepareJobId}`);
73
+ }
55
74
  const startedAt = data.started_at != null ? String(data.started_at) : null;
56
75
  if (startedAt) {
57
76
  parts.push(`started_at: ${startedAt}`);
@@ -78,7 +97,10 @@ export function formatJobStatusText(job_id, data) {
78
97
  if (panelTe != null) {
79
98
  parts.push(`panel_te: ${panelTe.toFixed(4)}`);
80
99
  }
81
- if (data.kernel_complete === true) {
100
+ if (awaitingFinalize) {
101
+ parts.push("kernel_complete — figures/finalize still pending (not fully done)");
102
+ }
103
+ else if (kernelComplete) {
82
104
  parts.push("kernel_complete");
83
105
  }
84
106
  }
package/dist/shared.js CHANGED
@@ -39,7 +39,7 @@ function newRequestId() {
39
39
  * X-Barsom-Client-Version so the server can annotate tool guidance with the
40
40
  * wrapper version each action requires. Keep in sync with package.json on bump.
41
41
  */
42
- export const CLIENT_VERSION = "0.23.5";
42
+ export const CLIENT_VERSION = "0.23.8";
43
43
  /** User-facing links; keep aligned with barivia.se / api.barivia.se. */
44
44
  export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
45
45
  /** Self-serve account dashboard (manage plan, billing, and API keys). */
@@ -77,6 +77,97 @@ export const LARGE_UPLOAD_BYTES = 64 * 1024 * 1024; // 64 MB
77
77
  export const PRESIGNED_PUT_TIMEOUT_MS = 30 * 60_000; // 30 min
78
78
  /** Poll window for the async stage_dataset job (large files take minutes). */
79
79
  export const POLL_STAGE_MAX_MS = 30 * 60_000; // 30 min
80
+ /** Normalize upload JSON after staging completes — never leave staging:true with train_ready. */
81
+ export function finalizeStagedUploadResponse(data, ready) {
82
+ const merged = ready
83
+ ? { ...data, ...ready }
84
+ : { ...data };
85
+ const trainReady = merged.train_ready === true || String(merged.status ?? "") === "ready";
86
+ if (trainReady) {
87
+ merged.status = ready?.status ?? merged.status ?? "ready";
88
+ merged.train_ready = true;
89
+ merged.staging = false;
90
+ // stage_job_id is only useful while staging is pending
91
+ if (ready?.stage_job_id == null)
92
+ delete merged.stage_job_id;
93
+ }
94
+ return merged;
95
+ }
96
+ /**
97
+ * After a small POST /v1/datasets upload, poll stage_job_id until the dataset is
98
+ * train-ready (mirrors the large-upload finalize path).
99
+ */
100
+ export async function awaitDatasetStageIfNeeded(data) {
101
+ const stageJobId = data.stage_job_id != null ? String(data.stage_job_id) : "";
102
+ if (!stageJobId) {
103
+ if (data.train_ready === true || data.status === "ready") {
104
+ return finalizeStagedUploadResponse(data);
105
+ }
106
+ return data;
107
+ }
108
+ if (data.train_ready === true || data.status === "ready") {
109
+ return finalizeStagedUploadResponse(data);
110
+ }
111
+ const poll = await pollUntilComplete(stageJobId, POLL_STAGE_MAX_MS);
112
+ if (poll.status === "failed") {
113
+ throw new Error(`Dataset staging failed (stage_job_id=${stageJobId}): ${poll.error ?? "unknown error"}`);
114
+ }
115
+ if (poll.status === "timeout") {
116
+ throw new Error(`Dataset staging timed out after ${POLL_STAGE_MAX_MS / 60000} min (stage_job_id=${stageJobId}). Poll jobs(action=status, job_id="${stageJobId}").`);
117
+ }
118
+ const datasetId = String(data.id ?? data.dataset_id ?? "");
119
+ if (datasetId) {
120
+ try {
121
+ const ready = (await apiCall("GET", `/v1/datasets/${datasetId}`));
122
+ return finalizeStagedUploadResponse(data, ready);
123
+ }
124
+ catch {
125
+ /* fall through */
126
+ }
127
+ }
128
+ return finalizeStagedUploadResponse({ ...data, status: "ready", train_ready: true });
129
+ }
130
+ /**
131
+ * Before train / CFD submit: ensure the dataset has finished staging.
132
+ * Prefer train_ready from GET; otherwise poll stage_job_id.
133
+ */
134
+ export async function ensureDatasetTrainReady(datasetId) {
135
+ const ds = (await apiCall("GET", `/v1/datasets/${datasetId}`));
136
+ if (ds.train_ready === true)
137
+ return;
138
+ if (String(ds.status) === "failed") {
139
+ const err = ds.ingest_error != null ? String(ds.ingest_error) : "staging failed";
140
+ throw new Error(`Dataset ${datasetId} staging failed: ${err}`);
141
+ }
142
+ const stageJobId = ds.stage_job_id != null ? String(ds.stage_job_id) : "";
143
+ if (stageJobId) {
144
+ const poll = await pollUntilComplete(stageJobId, POLL_STAGE_MAX_MS);
145
+ if (poll.status === "failed") {
146
+ throw new Error(`Dataset staging failed (stage_job_id=${stageJobId}): ${poll.error ?? "unknown error"}`);
147
+ }
148
+ if (poll.status === "timeout") {
149
+ throw new Error(`Dataset staging timed out (stage_job_id=${stageJobId}). Poll jobs(action=status, job_id="${stageJobId}").`);
150
+ }
151
+ return;
152
+ }
153
+ // No stage job listed — brief poll on GET for race windows
154
+ const deadline = Date.now() + 60_000;
155
+ while (Date.now() < deadline) {
156
+ await new Promise((r) => setTimeout(r, 1000));
157
+ const again = (await apiCall("GET", `/v1/datasets/${datasetId}`));
158
+ if (again.train_ready === true)
159
+ return;
160
+ if (String(again.status) === "failed") {
161
+ const err = again.ingest_error != null ? String(again.ingest_error) : "staging failed";
162
+ throw new Error(`Dataset ${datasetId} staging failed: ${err}`);
163
+ }
164
+ if (again.stage_job_id != null) {
165
+ await ensureDatasetTrainReady(datasetId);
166
+ return;
167
+ }
168
+ }
169
+ throw new Error(`Dataset ${datasetId} is not train_ready (status=${ds.status ?? "?"}). Wait for staging or poll datasets(action=get).`);
170
+ }
80
171
  export const BARSOM_VIZ_VIEWS = {
81
172
  trainingMonitor: "training-monitor",
82
173
  resultsExplorer: "results-explorer",
@@ -288,11 +379,51 @@ function enforceWorkspaceSandboxUpload() {
288
379
  return false;
289
380
  return true;
290
381
  }
382
+ /** Extra realpath roots allowed for uploads (comma- or colon-separated absolute paths). */
383
+ export function getWorkspaceExtraRoots() {
384
+ const raw = process.env.BARIVIA_WORKSPACE_EXTRA_ROOTS ?? "";
385
+ if (!raw.trim())
386
+ return [];
387
+ return raw
388
+ .split(/[,:]/)
389
+ .map((s) => s.trim())
390
+ .filter(Boolean)
391
+ .map((s) => path.resolve(s));
392
+ }
393
+ async function realpathIfExists(p) {
394
+ try {
395
+ return await fs.realpath(p);
396
+ }
397
+ catch {
398
+ return null;
399
+ }
400
+ }
401
+ /** True when `realPath` is under workspace root or any BARIVIA_WORKSPACE_EXTRA_ROOTS entry. */
402
+ export async function isPathUnderSandboxRoots(realPath, workspaceRoot) {
403
+ const candidates = [workspaceRoot, ...getWorkspaceExtraRoots()];
404
+ for (const root of candidates) {
405
+ const realRoot = await realpathIfExists(root);
406
+ if (!realRoot)
407
+ continue;
408
+ if (realPath === realRoot || realPath.startsWith(realRoot + path.sep))
409
+ return true;
410
+ }
411
+ return false;
412
+ }
413
+ function sandboxEscapeError(realPath, workspaceRoot) {
414
+ const extras = getWorkspaceExtraRoots();
415
+ const extraHint = extras.length > 0
416
+ ? ` Also checked BARIVIA_WORKSPACE_EXTRA_ROOTS (${extras.join(", ")}).`
417
+ : " Set BARIVIA_WORKSPACE_EXTRA_ROOTS to allow shared fixture dirs (e.g. a data/ sibling), or copy the file into the workspace.";
418
+ return new Error(`Resolved path escapes workspace — symlink resolves outside BARIVIA_WORKSPACE_ROOT` +
419
+ ` (${workspaceRoot}). Target: ${realPath}.${extraHint}`);
420
+ }
291
421
  /**
292
422
  * Resolves file_path for dataset upload. Security: auth before read, generic errors, reject "..".
293
423
  * Accepts: absolute paths, file:// URIs, relative paths (resolved against workspace root).
294
424
  * Pre-security-patch behavior: absolute paths worked without BARIVIA_WORKSPACE_ROOT.
295
- * Set BARIVIA_ENFORCE_WORKSPACE_SANDBOX=1 to require absolute paths to lie under the resolved workspace root.
425
+ * Set BARIVIA_ENFORCE_WORKSPACE_SANDBOX=1 to require absolute paths to lie under the resolved workspace root
426
+ * or an entry in BARIVIA_WORKSPACE_EXTRA_ROOTS (after realpath, so symlinks are allowed when the target is under an allowed root).
296
427
  */
297
428
  export async function resolveFilePathForUpload(filePath, mcpServer) {
298
429
  const trimmed = filePath.trim();
@@ -309,9 +440,8 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
309
440
  if (enforceWorkspaceSandboxUpload()) {
310
441
  const root = await getWorkspaceRootAsync(mcpServer);
311
442
  const real = await fs.realpath(p);
312
- const realRoot = await fs.realpath(root);
313
- if (real !== realRoot && !real.startsWith(realRoot + path.sep)) {
314
- throw new Error(`file:// paths outside the workspace are disabled (BARIVIA_ENFORCE_WORKSPACE_SANDBOX). Workspace: ${realRoot}`);
443
+ if (!(await isPathUnderSandboxRoots(real, root))) {
444
+ throw sandboxEscapeError(real, root);
315
445
  }
316
446
  const stat = await fs.stat(real);
317
447
  if (!stat.isFile()) {
@@ -324,7 +454,7 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
324
454
  catch (err) {
325
455
  if (err instanceof Error && err.message.includes("remote hosts"))
326
456
  throw err;
327
- if (err instanceof Error && err.message.includes("BARIVIA_ENFORCE_WORKSPACE_SANDBOX"))
457
+ if (err instanceof Error && err.message.includes("symlink resolves outside"))
328
458
  throw err;
329
459
  if (err instanceof Error && err.message.includes("regular file"))
330
460
  throw err;
@@ -339,11 +469,10 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
339
469
  real = await fs.realpath(trimmed);
340
470
  }
341
471
  catch {
342
- throw new Error(`File not accessible at absolute path. With BARIVIA_ENFORCE_WORKSPACE_SANDBOX, use a path under the workspace root (${root}).`);
472
+ throw new Error(`File not accessible at absolute path. With BARIVIA_ENFORCE_WORKSPACE_SANDBOX, use a path under the workspace root (${root}) or BARIVIA_WORKSPACE_EXTRA_ROOTS.`);
343
473
  }
344
- const realRoot = await fs.realpath(root);
345
- if (real !== realRoot && !real.startsWith(realRoot + path.sep)) {
346
- throw new Error(`Absolute paths outside the workspace are disabled (BARIVIA_ENFORCE_WORKSPACE_SANDBOX). Workspace root: ${realRoot}`);
474
+ if (!(await isPathUnderSandboxRoots(real, root))) {
475
+ throw sandboxEscapeError(real, root);
347
476
  }
348
477
  const stat = await fs.stat(real);
349
478
  if (!stat.isFile()) {
@@ -355,14 +484,14 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
355
484
  }
356
485
  const root = await getWorkspaceRootAsync(mcpServer);
357
486
  const resolved = path.resolve(root, trimmed);
487
+ // Lexical check against workspace root only (relative paths must stay under the case workspace).
358
488
  if (resolved !== root && !resolved.startsWith(root + path.sep)) {
359
489
  throw new Error("Path must be within the workspace directory.");
360
490
  }
361
491
  try {
362
492
  const real = await fs.realpath(resolved);
363
- const realRoot = await fs.realpath(root);
364
- if (real !== realRoot && !real.startsWith(realRoot + path.sep)) {
365
- throw new Error("Resolved path escapes workspace (e.g. symlink).");
493
+ if (!(await isPathUnderSandboxRoots(real, root))) {
494
+ throw sandboxEscapeError(real, root);
366
495
  }
367
496
  const stat = await fs.stat(real);
368
497
  if (!stat.isFile()) {
@@ -371,7 +500,8 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
371
500
  return real;
372
501
  }
373
502
  catch (err) {
374
- if (err instanceof Error && (err.message.includes("escapes") || err.message.includes("regular file"))) {
503
+ if (err instanceof Error &&
504
+ (err.message.includes("symlink resolves outside") || err.message.includes("regular file"))) {
375
505
  throw err;
376
506
  }
377
507
  throw new Error(`File not accessible. Easiest fix: pass an ABSOLUTE path (e.g. "/home/you/project/data.csv" or ` +
@@ -444,7 +574,15 @@ export function classifyApiError(status, bodyText, requestId) {
444
574
  }
445
575
  else if (status === 409) {
446
576
  cause = "client";
447
- hint = "The job may not be in the expected state.";
577
+ if (apiCode === "staging_not_ready") {
578
+ const stageId = parsed?.stage_job_id != null ? String(parsed.stage_job_id) : "";
579
+ hint = stageId
580
+ ? `Wait for stage_dataset to finish: jobs(action=monitor, job_id="${stageId}"), then retry train. Prefer datasets(action=get) train_ready=true.`
581
+ : "Wait for dataset staging (status=ready with staged_prefix / train_ready=true), then retry train.";
582
+ }
583
+ else {
584
+ hint = "The job may not be in the expected state.";
585
+ }
448
586
  retryable = false;
449
587
  }
450
588
  else if (status === 429) {
@@ -824,15 +962,30 @@ export function structuredTextResult(structuredContent, text, content) {
824
962
  }
825
963
  export async function pollUntilComplete(jobId, maxWaitMs = 30_000, intervalMs = 1000) {
826
964
  const start = Date.now();
965
+ /** Brief 404s happen when finalize_job_id is visible before the child row is readable. */
966
+ let notFoundStreak = 0;
967
+ const maxNotFoundRetries = 5;
827
968
  while (Date.now() - start < maxWaitMs) {
828
- const data = (await apiCall("GET", `/v1/jobs/${jobId}`));
829
- const status = data.status;
830
- if (status === "completed" || status === "failed" || status === "cancelled") {
831
- return {
832
- status,
833
- result_ref: data.result_ref,
834
- error: data.error,
835
- };
969
+ try {
970
+ const data = (await apiCall("GET", `/v1/jobs/${jobId}`));
971
+ notFoundStreak = 0;
972
+ const status = data.status;
973
+ if (status === "completed" || status === "failed" || status === "cancelled") {
974
+ return {
975
+ status,
976
+ result_ref: data.result_ref,
977
+ error: data.error,
978
+ };
979
+ }
980
+ }
981
+ catch (err) {
982
+ const status = err?.httpStatus;
983
+ if (status === 404 && notFoundStreak < maxNotFoundRetries) {
984
+ notFoundStreak += 1;
985
+ await new Promise((r) => setTimeout(r, intervalMs));
986
+ continue;
987
+ }
988
+ throw err;
836
989
  }
837
990
  await new Promise((r) => setTimeout(r, intervalMs));
838
991
  }
@@ -986,6 +1139,43 @@ export function inlineAttachBytesUsed() {
986
1139
  export function shouldFetchAllRemainingFigures(figures) {
987
1140
  return figures === "all" || figures === "images";
988
1141
  }
1142
+ /** Enum tokens accepted by results(figures). Prefer string "all"/"none"; arrays are for names/filenames. */
1143
+ const RESULTS_FIGURE_ENUMS = new Set([
1144
+ "default",
1145
+ "combined_only",
1146
+ "all",
1147
+ "images",
1148
+ "none",
1149
+ ]);
1150
+ /**
1151
+ * Coerce host-stringified figures args (e.g. `'["all"]'`) and singleton enum arrays
1152
+ * (`["all"]` → `"all"`) before Zod validation. Real filename arrays stay arrays.
1153
+ */
1154
+ export function coerceFiguresArg(v) {
1155
+ if (v === undefined || v === null)
1156
+ return v;
1157
+ let val = v;
1158
+ if (typeof val === "string") {
1159
+ const trimmed = val.trim();
1160
+ if ((trimmed.startsWith("[") && trimmed.endsWith("]")) ||
1161
+ (trimmed.startsWith('"') && trimmed.endsWith('"'))) {
1162
+ try {
1163
+ val = JSON.parse(trimmed);
1164
+ }
1165
+ catch {
1166
+ return v;
1167
+ }
1168
+ }
1169
+ }
1170
+ if (Array.isArray(val)) {
1171
+ const asStrings = val.map((x) => String(x));
1172
+ if (asStrings.length === 1 && RESULTS_FIGURE_ENUMS.has(asStrings[0])) {
1173
+ return asStrings[0];
1174
+ }
1175
+ return asStrings;
1176
+ }
1177
+ return val;
1178
+ }
989
1179
  /** Attach result figures with captions; tracks inlined names for dedup. */
990
1180
  export async function attachResultsImages(content, jobId, jobType, summary, figures, includeIndividual, inlinedImages) {
991
1181
  for (const name of getResultsImagesToFetch(jobType, summary, figures, includeIndividual)) {
@@ -38,7 +38,7 @@ export function registerAccountTool(server) {
38
38
  | Action | Use when |
39
39
  |--------|----------|
40
40
  | status | Before large jobs — see plan tier, GPU availability, queue depth, training time estimates, credit balance. When the API is down, returns calm \`{ok:false, retry_after_sec}\` instead of HTML. |
41
- | history | Viewing recent compute usage and credit spend |
41
+ | history | Viewing recent compute usage and credit spend (gateway-only; local/direct API soft-degrades to "unavailable" — use inventory/jobs list) |
42
42
  | inventory | Rebuild a durable markdown overview of datasets + recent jobs (agent-friendly org catalog) |
43
43
  | add_funds | Getting instructions to add credits |
44
44
  | health | Diagnose API reachability during outages — GET /health + /ready; does **not** probe R2/object storage |
@@ -157,11 +157,32 @@ NOT FOR: Training itself — use train(action=map). This tool only manages the a
157
157
  return { content: [{ type: "text", text: lines.join("\n") }] };
158
158
  }
159
159
  if (action === "history") {
160
- const data = await apiCall("GET", `/v1/compute/history?limit=${limit || 10}`);
161
- const history = data.history.map((h) => `- ${h.started_at} | ${h.tier} | ${h.duration_minutes} min | $${(h.credits_charged / 100).toFixed(2)}`).join("\n");
162
- return {
163
- content: [{ type: "text", text: `Credit Balance: $${(data.credit_balance_cents / 100).toFixed(2)}\n\nRecent Usage:\n${history}` }]
164
- };
160
+ // Gateway serves /v1/compute/history (D1 leases). Local direct API has no handler → 404.
161
+ try {
162
+ const data = await apiCall("GET", `/v1/compute/history?limit=${limit || 10}`);
163
+ const rows = Array.isArray(data?.history) ? data.history : [];
164
+ const history = rows.length === 0
165
+ ? "(none)"
166
+ : rows.map((h) => `- ${h.started_at} | ${h.tier} | ${h.duration_minutes} min | $${(h.credits_charged / 100).toFixed(2)}`).join("\n");
167
+ const bal = data?.credit_balance_cents != null
168
+ ? `Credit Balance: $${(Number(data.credit_balance_cents) / 100).toFixed(2)}\n\n`
169
+ : "";
170
+ return {
171
+ content: [{ type: "text", text: `${bal}Recent Usage:\n${history}` }]
172
+ };
173
+ }
174
+ catch (e) {
175
+ const status = e?.httpStatus;
176
+ if (status === 404 || status === 410) {
177
+ return {
178
+ content: [{
179
+ type: "text",
180
+ text: "Usage history unavailable on this API endpoint (compute-lease history is gateway-only). Use account(action=inventory) or jobs(action=list) for recent job activity.",
181
+ }]
182
+ };
183
+ }
184
+ throw e;
185
+ }
165
186
  }
166
187
  if (action === "inventory") {
167
188
  const jobLimit = limit && limit > 0 ? Math.min(Math.floor(limit), 100) : 50;
@@ -4,7 +4,7 @@ import { gzipSync } from "node:zlib";
4
4
  import { createHash } from "node:crypto";
5
5
  import { z } from "zod";
6
6
  import { registerAuditedTool } from "../audit.js";
7
- import { apiCall, getWorkspaceRootAsync, resolveFilePathForUpload, textResult, structuredTextResult, pollUntilComplete, POLL_DERIVE_MAX_MS, POLL_ANALYZE_MAX_MS, UPLOAD_DATASET_TIMEOUT_MS, LARGE_UPLOAD_BYTES, PRESIGNED_PUT_TIMEOUT_MS, POLL_STAGE_MAX_MS, streamFileSha256, putPresignedStream, resolveUploadContentType, suggestDatasetPreview, } from "../shared.js";
7
+ import { apiCall, getWorkspaceRootAsync, resolveFilePathForUpload, textResult, structuredTextResult, pollUntilComplete, POLL_DERIVE_MAX_MS, POLL_ANALYZE_MAX_MS, UPLOAD_DATASET_TIMEOUT_MS, LARGE_UPLOAD_BYTES, PRESIGNED_PUT_TIMEOUT_MS, POLL_STAGE_MAX_MS, awaitDatasetStageIfNeeded, streamFileSha256, putPresignedStream, resolveUploadContentType, suggestDatasetPreview, } from "../shared.js";
8
8
  import { GZIP_UPLOAD_HINT } from "../job_status_format.js";
9
9
  import { formatDatasetInventoryLine, formatTags } from "../inventory_format.js";
10
10
  import { PERIODICITY_ROW_ORDER_NOTE, SMALL_N_SOFT, buildCpuScaleGuidanceLines, buildSmallNGuardrailLines, } from "../training_scale_guidance.js";
@@ -165,7 +165,7 @@ Step 1 (after upload): always datasets(action=preview) to verify column types an
165
165
  action=preview: Show columns, stats, sample rows, cyclic/datetime detections. ALWAYS preview before train(action=map) on an unfamiliar dataset.
166
166
  action=analyze: Pre-training analysis on numeric columns — Pearson correlation, autocorrelation periodicity scores, and column recommendations (train, consider_dropping, project_later, low_variance). Use to choose which columns to include in training and which to project onto the map after training.
167
167
  action=list: List all datasets belonging to the organisation with id, name, rows, cols, created_at, description, tags (use tags/description or id to distinguish datasets with the same name).
168
- action=get: Fetch one dataset by dataset_id — returns status, staged_prefix, staged_version, ingest_error, description/tags, and stage_job_id when staging is pending.
168
+ action=get: Fetch one dataset by dataset_id — returns status, train_ready (true only when status=ready AND staged_prefix is set), staged_prefix, staged_version, ingest_error, description/tags, and stage_job_id when staging is pending. Train only when train_ready=true (do not treat bare status=ready as enough).
169
169
  action=update: PATCH name/description/tags on an existing dataset.
170
170
  action=upload: Optional description and tags help later inventory (also settable via update).
171
171
  DO NOT AUTO-SUBSET: Call action=subset only when the user explicitly requests a filter, slice, or random sample. Default path is upload → preview/analyze → train on the full dataset_id (GPU handles scale). Never downsample to "save time" or "be helpful".
@@ -230,7 +230,7 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
230
230
  })
231
231
  .optional()
232
232
  .describe("Deprecated — use filters instead. Single filter condition."),
233
- expression: z.string().optional().describe("action=add_expression: math expression referencing column names (e.g. revenue/cost, log(price), rolling_mean(vol, 20))"),
233
+ expression: z.string().optional().describe("action=add_expression: math expression referencing column names (e.g. revenue/cost, log(price), rolling_mean(vol, 20)). Awkward names (spaces/parens): quote with backticks, e.g. `P1 (bar)` / 2"),
234
234
  options: z
235
235
  .object({
236
236
  missing: z.enum(["skip", "zero", "interpolate"]).optional(),
@@ -377,7 +377,7 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
377
377
  gzHeaders["X-Dataset-Description"] = description;
378
378
  if (tags !== undefined)
379
379
  gzHeaders["X-Dataset-Tags"] = JSON.stringify(tags);
380
- const data = (await apiCall("POST", "/v1/datasets", gzBytes, gzHeaders, UPLOAD_DATASET_TIMEOUT_MS));
380
+ const data = await awaitDatasetStageIfNeeded((await apiCall("POST", "/v1/datasets", gzBytes, gzHeaders, UPLOAD_DATASET_TIMEOUT_MS)));
381
381
  const gid = data.id ?? data.dataset_id;
382
382
  if (gid != null)
383
383
  data.suggested_next_step = `${suggestDatasetPreview(String(gid))} to inspect columns before training.`;
@@ -410,7 +410,7 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
410
410
  uploadBody = gzipSync(Buffer.from(body, "utf-8"));
411
411
  uploadHeaders["Content-Encoding"] = "gzip";
412
412
  }
413
- const data = (await apiCall("POST", "/v1/datasets", uploadBody, uploadHeaders, UPLOAD_DATASET_TIMEOUT_MS));
413
+ const data = await awaitDatasetStageIfNeeded((await apiCall("POST", "/v1/datasets", uploadBody, uploadHeaders, UPLOAD_DATASET_TIMEOUT_MS)));
414
414
  const id = data.id ?? data.dataset_id;
415
415
  if (id != null)
416
416
  data.suggested_next_step = `${suggestDatasetPreview(String(id))} to inspect columns before training.`;
@@ -732,16 +732,20 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
732
732
  throw new Error("datasets(get) requires dataset_id");
733
733
  const ds = (await apiCall("GET", `/v1/datasets/${dataset_id}`));
734
734
  const tagStr = formatTags(ds.tags);
735
+ const trainReady = ds.train_ready === true;
735
736
  const lines = [
736
737
  `Dataset: ${ds.name ?? "?"} (${ds.id ?? dataset_id})`,
737
- `Status: ${ds.status ?? "ready"}`,
738
+ `Status: ${ds.status ?? "unknown"}`,
739
+ `Train ready: ${trainReady ? "yes" : "no"}`,
738
740
  `Rows × cols: ${ds.rows ?? "?"} × ${ds.cols ?? "?"}`,
739
741
  ds.size_bytes != null ? `Size: ${Number(ds.size_bytes).toLocaleString()} bytes` : "",
740
742
  ds.description != null && String(ds.description).trim() !== "" ? `Description: ${String(ds.description)}` : "",
741
743
  tagStr ? `Tags: ${tagStr}` : "",
742
744
  ds.staged_prefix != null ? `Staged prefix: ${String(ds.staged_prefix)}` : "",
743
745
  ds.staged_version != null ? `Staged version: ${String(ds.staged_version)}` : "",
744
- ds.stage_job_id != null ? `Stage job: ${String(ds.stage_job_id)} (poll jobs(action=status))` : "",
746
+ !trainReady && ds.stage_job_id != null
747
+ ? `Stage job: ${String(ds.stage_job_id)} (poll jobs(action=status) until train_ready)`
748
+ : "",
745
749
  cleanNullable(ds.ingest_error) ? `Ingest error: ${cleanNullable(ds.ingest_error)}` : "",
746
750
  ds.created_at != null ? `Created: ${String(ds.created_at)}` : "",
747
751
  ].filter(Boolean);
@@ -79,7 +79,7 @@ action=predict: Score rows against the trained map.
79
79
  - "compact" → predictions.csv (row_id, bmu_x, bmu_y, bmu_node_index, cluster_id [, quantization_error, potential_anomaly]).
80
80
  - "annotated" → annotated.csv (full source CSV with bmu_x, bmu_y, bmu_node_index, cluster_id appended). Requires a dataset (no inline rows).
81
81
  Regime (auto): if the resolved dataset is the parent training dataset, regime="training" and QE / qe_p95 / potential_anomaly are omitted (QE on training data is fitting error, not generalisation — use a held-out dataset for quality). Otherwise regime="new" and full QE columns are returned.
82
- Schema rules: dataset / batch rows must match the trained feature set including cyclic-expanded columns (e.g. key_cos, key_sin, not raw key). For a single inline row, the proxy uses the stateless model endpoint raw categorical strings are allowed for baseline categorical_features models, and raw cyclic inputs are expanded from training config.
82
+ Schema rules: prefer dataset_id for scoring. Inline rows may use raw cyclic keys (e.g. hour) the API/worker expands them from training config to hour_cos/hour_sin; pre-expanded cos/sin columns also work. For a single inline row, the proxy uses the stateless model endpoint (same cyclic expansion). Raw categorical strings are allowed for baseline categorical_features models on that single-row path.
83
83
  Routing: prefer dataset_id for many rows or whenever the map uses irregular SIOM / GeneralTopology layouts — the async worker path is the supported batch scorer. Single-row rows take a fast stateless path that may return invalid_inference_input on some topologies; if so, retry with dataset_id (a one-row dataset is fine). FLooP-SIOM: use dataset_id predict first.
84
84
  When the scored set has at most ${PREDICT_PREVIEW_ROW_CAP} rows, completed responses include a short per-line preview in the tool text for chat agents.
85
85
 
@@ -348,32 +348,64 @@ action=report: Returns a report manifest for the given job_id (job must be compl
348
348
  const download_urls = (results.download_urls ?? {});
349
349
  const figure_manifest = (results.figure_manifest ?? []);
350
350
  const cluster_summary = results.cluster_summary;
351
+ const summary = (results.summary ?? {});
351
352
  const expires_in = results.expires_in ?? 900;
353
+ const downloadKeys = Object.keys(download_urls);
354
+ const clusterSnippet = Array.isArray(cluster_summary) && cluster_summary.length > 0
355
+ ? cluster_summary.slice(0, 3).map((c) => {
356
+ const id = c.cluster_id ?? c.id ?? "?";
357
+ const n = c.n_nodes ?? c.size ?? "?";
358
+ return { cluster_id: id, n_nodes: n };
359
+ })
360
+ : [];
352
361
  const lines = [
353
362
  `Report manifest — job: ${job_id}`,
354
363
  `Use these artifacts to build your own report (Quarto, Jupyter, script). URLs expire in ${expires_in}s.`,
355
364
  "",
356
365
  "Figures (figure_manifest → download_urls):",
357
- ...figure_manifest.slice(0, 12).map((e) => ` ${e.logical_name} → ${e.filename}`),
366
+ ...(figure_manifest.length
367
+ ? figure_manifest.slice(0, 12).map((e) => ` ${e.logical_name} → ${e.filename}`)
368
+ : [" (none listed — small grids may omit PNGs; use cluster_summary / summary metrics below)"]),
358
369
  figure_manifest.length > 12 ? ` ... and ${figure_manifest.length - 12} more` : "",
359
370
  "",
360
371
  "Key download URLs (use get_result_image or download_urls from results(action=get)):",
361
- ...Object.keys(download_urls)
362
- .filter((k) => !k.endsWith(".json") || k === "cluster_summary.json")
363
- .slice(0, 8)
364
- .map((k) => ` ${k}: ${download_urls[k]?.slice(0, 60)}...`),
372
+ ...(downloadKeys.length
373
+ ? downloadKeys
374
+ .filter((k) => !k.endsWith(".json") || k === "cluster_summary.json")
375
+ .slice(0, 8)
376
+ .map((k) => ` ${k}: ${download_urls[k]?.slice(0, 60)}...`)
377
+ : [" (no presigned URLs — artifacts may still be in summary / cluster_summary inline)"]),
365
378
  "",
366
379
  cluster_summary?.length
367
- ? `Cluster summary: ${cluster_summary.length} clusters (in response or GET .../cluster_summary).`
380
+ ? `Cluster summary: ${cluster_summary.length} clusters (inline snippet below; full via GET .../cluster_summary or download_urls.cluster_summary.json).`
368
381
  : "Cluster summary: not available for this job (train with current worker to get it).",
382
+ ...(clusterSnippet.length
383
+ ? ["Inline cluster_summary (first 3):", ...clusterSnippet.map((c) => ` cluster ${c.cluster_id}: n_nodes=${c.n_nodes}`)]
384
+ : []),
369
385
  "",
370
386
  "Metrics: in summary (quantization_error, topographic_error, explained_variance, silhouette, etc.) or GET .../quality-report.",
387
+ summary.quantization_error != null ? ` QE=${summary.quantization_error}` : "",
388
+ summary.topographic_error != null ? ` TE=${summary.topographic_error}` : "",
371
389
  ];
372
390
  const manifestText = lines.filter(Boolean).join("\n");
373
391
  return {
374
392
  content: [
375
393
  { type: "text", text: manifestText },
376
- { type: "text", text: "Structured manifest (for automation): " + JSON.stringify({ job_id, figure_manifest, has_cluster_summary: !!cluster_summary?.length, download_url_keys: Object.keys(download_urls) }) },
394
+ {
395
+ type: "text",
396
+ text: "Structured manifest (for automation): " +
397
+ JSON.stringify({
398
+ job_id,
399
+ figure_manifest,
400
+ has_cluster_summary: !!cluster_summary?.length,
401
+ cluster_summary_snippet: clusterSnippet,
402
+ download_url_keys: downloadKeys,
403
+ summary_metrics: {
404
+ quantization_error: summary.quantization_error ?? null,
405
+ topographic_error: summary.topographic_error ?? null,
406
+ },
407
+ }),
408
+ },
377
409
  ],
378
410
  };
379
411
  });
@@ -2,7 +2,7 @@ import path from "node:path";
2
2
  import fs from "node:fs/promises";
3
3
  import { z } from "zod";
4
4
  import { registerAuditedTool } from "../audit.js";
5
- import { apiCall, apiRawCall, getWorkspaceRootAsync, sandboxPath, pollUntilComplete, tryAttachImage, resetInlineAttachBudget, attachResultsImages, resolveOutputDpi, POLL_RECOLOR_MAX_MS, fmtDensityDiffNode, getCaptionForImage, shouldFetchAllRemainingFigures, mimeForFilename, structuredTextResult, } from "../shared.js";
5
+ import { apiCall, apiRawCall, getWorkspaceRootAsync, sandboxPath, pollUntilComplete, tryAttachImage, resetInlineAttachBudget, attachResultsImages, resolveOutputDpi, POLL_RECOLOR_MAX_MS, fmtDensityDiffNode, getCaptionForImage, shouldFetchAllRemainingFigures, mimeForFilename, structuredTextResult, coerceFiguresArg, } from "../shared.js";
6
6
  export function registerResultsTool(server) {
7
7
  registerAuditedTool(server, "results", `Retrieve, recolor, download, or export figures and metrics for a completed map job. Presentation and artifact access only — operations on the frozen map (predict, compare, project, impute_column, transition_flow) live in **inference**.
8
8
 
@@ -20,21 +20,21 @@ ONLY call this after jobs(action=status) returns "completed".
20
20
  ESCALATION: If job not found, verify job_id. If "job not complete", poll with jobs(action=status).
21
21
 
22
22
  action=get: Returns text summary with quality metrics and inline images.
23
- - figures: omit = combined only. "all" = all published plots (excludes unpublished cyclic cos/sin expansions unless train used viz_upload_cyclic_components=true). "none" = metrics text only, no images (recommended for sweeps and LLM clients to keep tool payloads small). Array = specific logical names (combined, umatrix, hit_histogram, learning_curve, correlation, divergence_kl, component_1..N). Request unpublished cyclic planes via results(recolor, figures=[component_N, ...]).
23
+ - figures: omit = combined only. Prefer string "all" / "none" (not an array). "all" = all published plots (excludes unpublished cyclic cos/sin expansions unless train used viz_upload_cyclic_components=true). "none" = metrics text only, no images (recommended for sweeps and LLM clients to keep tool payloads small). Array = specific logical names (combined, umatrix, hit_histogram, learning_curve, correlation, divergence_kl, component_1..N) or download filenames. Hosts that stringify arrays are coerced. Request unpublished cyclic planes via results(recolor, figures=[component_N, ...]).
24
24
  - include_individual: if true (and figures omitted), inlines every component plane.
25
25
  - After recolor or project, use the job_id returned by that operation to get the new artifacts, not the original training job_id.
26
26
  - After showing results, guide the user: QE interpretation, whether to retrain, which features to explore.
27
27
  - METRIC INTERPRETATION: QE lower=better (<1.5 good) | TE<0.1 good | Explained variance>0.7 good | Silhouette higher=better | Davies-Bouldin lower=better.
28
28
 
29
- action=export: Structured data exports. Use export_type= to choose what to export.
30
- - export_type=training_log: learning curve sparklines + plot. Diagnose convergence/plateau/divergence.
29
+ action=export: Structured data exports (not the same as download). Use export_type= to choose what to export.
30
+ - export_type=training_log: learning curve sparklines + plot. Diagnose convergence/plateau/divergence. Prefer this over download for the training log.
31
31
  - export_type=weights: full weight matrix + normalization stats. For external analysis or custom viz.
32
32
  - export_type=nodes: per-node hit count + feature stats. Profile clusters and operating modes.
33
33
 
34
- action=download: Save figures to disk. Use so user can open, share, or version files locally.
34
+ action=download: Save figures/artifacts to disk (deduped file list). Use so user can open, share, or version files locally.
35
35
  - folder: e.g. "." or "./results". Relative to the client's working directory (or MCP workspace). Each job lands in a dedicated subfolder under folder: {job_type}_{label} when label was set at train time, otherwise {job_type}_{job_id_prefix} — so parallel downloads never overwrite summary.json or figure names.
36
- - figures: "all" (default) or array of filenames.
37
- - include_json: when true, also saves summary.json (and adds it to the download list if figures omitted).
36
+ - figures: "all" (default) downloads published image files; OR a string array of exact artifact filenames from the job (e.g. ["predictions.csv"], ["combined.png","umatrix.png"]). Enum tokens like "none" are for action=get, not download.
37
+ - include_json: when true, also saves summary.json and other JSON artifacts (use this when you need CSVs listed in summary.files without naming each figure).
38
38
 
39
39
  action=recolor: Change colormap or output format — no retraining. Returns a new job_id; auto-polls 60s.
40
40
  AFTER: use results(action=get, job_id=NEW_JOB_ID).
@@ -47,10 +47,10 @@ NOT FOR: Jobs that haven't completed (use jobs(action=status) first), or tempora
47
47
  .enum(["get", "recolor", "download", "export", "feature_divergences"])
48
48
  .describe("get: inline results + metrics; recolor: new colormap/format (async); download: save to disk; export: structured data; feature_divergences: KL/W1 on component planes (async)."),
49
49
  job_id: z.string().describe("Job ID of a completed job"),
50
- figures: z
50
+ figures: z.preprocess(coerceFiguresArg, z
51
51
  .union([z.enum(["default", "combined_only", "all", "images", "none"]), z.array(z.string())])
52
52
  .optional()
53
- .describe("action=get: omit=combined only; 'all'=all plots; 'none'=metrics text only (no images, ideal for sweeps and LLM clients); array=specific (combined,umatrix,hit_histogram,learning_curve,correlation,divergence_kl,component_1..N). action=download: 'all'=all image files; array=specific filenames."),
53
+ .describe("Prefer string 'all'/'none'. action=get: omit=combined only; 'all'=all plots; 'none'=metrics text only; array=logical names (combined,umatrix,…). action=download: 'all'=image files; array=exact filenames (e.g. combined.png). Stringified JSON arrays from hosts are accepted.")),
54
54
  include_individual: z
55
55
  .boolean()
56
56
  .optional()
@@ -631,6 +631,7 @@ NOT FOR: Jobs that haven't completed (use jobs(action=status) first), or tempora
631
631
  else {
632
632
  toDownload = files.filter(isImage);
633
633
  }
634
+ toDownload = [...new Set(toDownload)];
634
635
  let resolvedDir = sandboxPath(folder, await getWorkspaceRootAsync(server));
635
636
  // Always namespace each job's files into its own subfolder so that
636
637
  // downloading multiple jobs (or job types) into the same folder never
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { registerAuditedTool } from "../audit.js";
3
- import { apiCall } from "../shared.js";
3
+ import { apiCall, ensureDatasetTrainReady } from "../shared.js";
4
4
  import { buildTrainSubmitExtras } from "../train_submit_message.js";
5
5
  import { TRAINING_MONITOR_URI, buildTrainingMonitorResult, } from "./training_monitor.js";
6
6
  import { TRAINING_INPUT_SCHEMA, buildTrainMapParams, buildFloopParams, fetchTrainingPresets, assertValidNormalizationArgs, } from "./training_core.js";
@@ -52,6 +52,7 @@ function assertImputeTrainingArgs(args) {
52
52
  }
53
53
  /** Shared POST /v1/jobs + train submit message extras (map/siom/impute and floop). */
54
54
  async function submitTrainJob(opts) {
55
+ await ensureDatasetTrainReady(opts.dataset_id);
55
56
  const submitBody = { dataset_id: opts.dataset_id, params: opts.params };
56
57
  if (opts.label && opts.label.trim() !== "")
57
58
  submitBody.label = opts.label;
@@ -2,9 +2,10 @@ import { buildVizStandaloneUrl } from "./viz_links.js";
2
2
  export function buildTrainSubmitExtras(opts) {
3
3
  const { newJobId, totalRows, hasTransforms, prepareJobId, variantPrefix, paramSummary, impute, resultsHint } = opts;
4
4
  const monitorUrl = buildVizStandaloneUrl("training-monitor", newJobId);
5
+ const prepareKind = impute ? "prepare_impute_matrix" : "prepare_training_matrix";
5
6
  let msg = `Job submitted (${variantPrefix}, ${paramSummary}). `;
6
7
  if (prepareJobId) {
7
- msg += `Preprocessing job prepare_training_matrix (${prepareJobId}) runs on worker-io first; train job ${newJobId} starts after it completes. Poll jobs(action=status, job_id="${prepareJobId}") until completed, then poll the train job. `;
8
+ msg += `Preprocessing job ${prepareKind} (${prepareJobId}) runs on worker-io first; train job ${newJobId} starts after it completes. Poll jobs(action=status, job_id="${prepareJobId}") or jobs(action=status, job_id="${newJobId}") (shows prepare_job_id + waiting_for_prepare) until prepare completes, then poll the train job. `;
8
9
  }
9
10
  msg += `Training monitor opened for job ${newJobId}`;
10
11
  if (monitorUrl)
@@ -25,7 +25,8 @@ export function teCurveLabel(base) {
25
25
  * epochs=[N,0], so convergence_errors is empty while ordering has many batch points.
26
26
  */
27
27
  export function isFloopJob(data) {
28
- const phase = String(data.training_progress_phase ?? data.progress_phase ?? "").toLowerCase();
28
+ const rawPhase = String(data.training_progress_phase ?? data.progress_phase ?? "").toLowerCase();
29
+ const phase = rawPhase === "missing" ? "" : rawPhase;
29
30
  if (phase === "floop")
30
31
  return true;
31
32
  const jt = String(data.job_type ?? "").toLowerCase();
@@ -139,7 +139,7 @@ for compatibility with Zod schema generation. Both are functionally equivalent f
139
139
  - "app": Tool callable by the app from this server only`)});v.object({mimeTypes:v.array(v.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')});v.object({method:v.literal("ui/message"),params:v.object({role:v.literal("user").describe('Message role, currently only "user" is supported.'),content:v.array(fo).describe("Message content blocks (text, image, etc.).")})});v.object({method:v.literal("ui/notifications/sandbox-resource-ready"),params:v.object({html:v.string().describe("HTML content to load into the inner iframe."),sandbox:v.string().optional().describe("Optional override for the inner iframe's sandbox attribute."),csp:fd.optional().describe("CSP configuration from resource metadata."),permissions:md.optional().describe("Sandbox permissions from resource metadata.")})});var TT=v.object({method:v.literal("ui/notifications/tool-result"),params:Na.describe("Standard MCP tool execution result.")}),h$=v.object({toolInfo:v.object({id:oo.optional().describe("JSON-RPC id of the tools/call request."),tool:gu.describe("Tool definition including name, inputSchema, etc.")}).optional().describe("Metadata of the tool call that instantiated this App."),theme:mT.optional().describe("Current color theme preference."),styles:kT.optional().describe("Style configuration for theming the app."),displayMode:Qr.optional().describe("How the UI is currently displayed."),availableDisplayModes:v.array(Qr).optional().describe("Display modes the host supports."),containerDimensions:v.union([v.object({height:v.number().describe("Fixed container height in pixels.")}),v.object({maxHeight:v.union([v.number(),v.undefined()]).optional().describe("Maximum container height in pixels.")})]).and(v.union([v.object({width:v.number().describe("Fixed container width in pixels.")}),v.object({maxWidth:v.union([v.number(),v.undefined()]).optional().describe("Maximum container width in pixels.")})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
140
140
  container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:v.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:v.string().optional().describe("User's timezone in IANA format."),userAgent:v.string().optional().describe("Host application identifier."),platform:v.union([v.literal("web"),v.literal("desktop"),v.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:v.object({touch:v.boolean().optional().describe("Whether the device supports touch input."),hover:v.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:v.object({top:v.number().describe("Top safe area inset in pixels."),right:v.number().describe("Right safe area inset in pixels."),bottom:v.number().describe("Bottom safe area inset in pixels."),left:v.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),ZT=v.object({method:v.literal("ui/notifications/host-context-changed"),params:h$.describe("Partial context update containing only changed fields.")});v.object({method:v.literal("ui/update-model-context"),params:v.object({content:v.array(fo).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:v.record(v.string(),v.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})});v.object({method:v.literal("ui/initialize"),params:v.object({appInfo:Ea.describe("App identification (name and version)."),appCapabilities:IT.describe("Features and capabilities this app provides."),protocolVersion:v.string().describe("Protocol version this app supports.")})});var PT=v.object({protocolVersion:v.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Ea.describe("Host application identification and version."),hostCapabilities:ST.describe("Features and capabilities provided by the host."),hostContext:h$.describe("Rich context about the host environment.")}).passthrough();class ET extends Ux{_appInfo;_capabilities;options;_hostCapabilities;_hostInfo;_hostContext;constructor(t,i={},r={autoResize:!0}){super(r),this._appInfo=t,this._capabilities=i,this.options=r,this.setRequestHandler(Ua,n=>(console.log("Received ping:",n.params),{})),this.onhostcontextchanged=()=>{}}getHostCapabilities(){return this._hostCapabilities}getHostVersion(){return this._hostInfo}getHostContext(){return this._hostContext}set ontoolinput(t){this.setNotificationHandler(_T,i=>t(i.params))}set ontoolinputpartial(t){this.setNotificationHandler(bT,i=>t(i.params))}set ontoolresult(t){this.setNotificationHandler(TT,i=>t(i.params))}set ontoolcancelled(t){this.setNotificationHandler($T,i=>t(i.params))}set onhostcontextchanged(t){this.setNotificationHandler(ZT,i=>{this._hostContext={...this._hostContext,...i.params},t(i.params)})}set onteardown(t){this.setRequestHandler(wT,(i,r)=>t(i.params,r))}set oncalltool(t){this.setRequestHandler(xp,(i,r)=>t(i.params,r))}set onlisttools(t){this.setRequestHandler(Ip,(i,r)=>t(i.params,r))}assertCapabilityForMethod(t){}assertRequestHandlerCapability(t){switch(t){case"tools/call":case"tools/list":if(!this._capabilities.tools)throw Error(`Client does not support tool capability (required for ${t})`);return;case"ping":case"ui/resource-teardown":return;default:throw Error(`No handler for method ${t} registered`)}}assertNotificationCapability(t){}assertTaskCapability(t){throw Error("Tasks are not supported in MCP Apps")}assertTaskHandlerCapability(t){throw Error("Task handlers are not supported in MCP Apps")}async callServerTool(t,i){return await this.request({method:"tools/call",params:t},Na,i)}sendMessage(t,i){return this.request({method:"ui/message",params:t},vT,i)}sendLog(t){return this.notification({method:"notifications/message",params:t})}updateModelContext(t,i){return this.request({method:"ui/update-model-context",params:t},tu,i)}openLink(t,i){return this.request({method:"ui/open-link",params:t},gT,i)}sendOpenLink=this.openLink;requestDisplayMode(t,i){return this.request({method:"ui/request-display-mode",params:t},xT,i)}sendSizeChanged(t){return this.notification({method:"ui/notifications/size-changed",params:t})}setupSizeChangedNotifications(){let t=!1,i=0,r=0,n=()=>{t||(t=!0,requestAnimationFrame(()=>{t=!1;let a=document.documentElement,s=a.style.width,l=a.style.height;a.style.width="fit-content",a.style.height="fit-content";let c=a.getBoundingClientRect();a.style.width=s,a.style.height=l;let m=window.innerWidth-a.clientWidth,f=Math.ceil(c.width+m),h=Math.ceil(c.height);(f!==i||h!==r)&&(i=f,r=h,this.sendSizeChanged({width:f,height:h}))}))};n();let o=new ResizeObserver(n);return o.observe(document.documentElement),o.observe(document.body),()=>o.disconnect()}async connect(t=new Ox(window.parent,window.parent),i){await super.connect(t);try{let r=await this.request({method:"ui/initialize",params:{appCapabilities:this._capabilities,appInfo:this._appInfo,protocolVersion:jx}},PT,i);if(r===void 0)throw Error(`Server sent invalid initialize result: ${r}`);this._hostCapabilities=r.hostCapabilities,this._hostInfo=r.hostInfo,this._hostContext=r.hostContext,await this.notification({method:"ui/notifications/initialized"}),this.options?.autoResize&&this.setupSizeChangedNotifications()}catch(r){throw this.close(),r}}}const UT=!0,ut="u-",NT="uplot",OT=ut+"hz",jT=ut+"vt",DT=ut+"title",RT=ut+"wrap",AT=ut+"under",CT=ut+"over",LT=ut+"axis",bi=ut+"off",MT=ut+"select",FT=ut+"cursor-x",JT=ut+"cursor-y",qT=ut+"cursor-pt",VT=ut+"legend",WT=ut+"live",HT=ut+"inline",BT=ut+"series",GT=ut+"marker",Wf=ut+"label",KT=ut+"value",Dr="width",Rr="height",Nr="top",Hf="bottom",er="left",rl="right",pd="#000",Bf=pd+"0",ol="mousemove",Gf="mousedown",al="mouseup",Kf="mouseenter",Yf="mouseleave",Xf="dblclick",YT="resize",XT="scroll",Qf="change",wa="dppxchange",hd="--",br=typeof window<"u",zl=br?document:null,ir=br?window:null,QT=br?navigator:null;let we,na;function Tl(){let e=devicePixelRatio;we!=e&&(we=e,na&&Pl(Qf,na,Tl),na=matchMedia(`(min-resolution: ${we-.001}dppx) and (max-resolution: ${we+.001}dppx)`),wi(Qf,na,Tl),ir.dispatchEvent(new CustomEvent(wa)))}function Vt(e,t){if(t!=null){let i=e.classList;!i.contains(t)&&i.add(t)}}function Zl(e,t){let i=e.classList;i.contains(t)&&i.remove(t)}function Ce(e,t,i){e.style[t]=i+"px"}function bn(e,t,i,r){let n=zl.createElement(e);return t!=null&&Vt(n,t),i?.insertBefore(n,r),n}function an(e,t){return bn("div",e,t)}const em=new WeakMap;function Pn(e,t,i,r,n){let o="translate("+t+"px,"+i+"px)",a=em.get(e);o!=a&&(e.style.transform=o,em.set(e,o),t<0||i<0||t>r||i>n?Vt(e,bi):Zl(e,bi))}const tm=new WeakMap;function nm(e,t,i){let r=t+i,n=tm.get(e);r!=n&&(tm.set(e,r),e.style.background=t,e.style.borderColor=i)}const im=new WeakMap;function rm(e,t,i,r){let n=t+""+i,o=im.get(e);n!=o&&(im.set(e,n),e.style.height=i+"px",e.style.width=t+"px",e.style.marginLeft=r?-t/2+"px":0,e.style.marginTop=r?-i/2+"px":0)}const gd={passive:!0},e6={...gd,capture:!0};function wi(e,t,i,r){t.addEventListener(e,i,r?e6:gd)}function Pl(e,t,i,r){t.removeEventListener(e,i,gd)}br&&Tl();function $n(e,t,i,r){let n;i=i||0,r=r||t.length-1;let o=r<=2147483647;for(;r-i>1;)n=o?i+r>>1:Ht((i+r)/2),t[n]<e?i=n:r=n;return e-t[i]<=t[r]-e?i:r}function g$(e){return(i,r,n)=>{let o=-1,a=-1;for(let s=r;s<=n;s++)if(e(i[s])){o=s;break}for(let s=n;s>=r;s--)if(e(i[s])){a=s;break}return[o,a]}}const v$=e=>e!=null,_$=e=>e!=null&&e>0,Is=g$(v$),t6=g$(_$);function n6(e,t,i,r=0,n=!1){let o=n?t6:Is,a=n?_$:v$;[t,i]=o(e,t,i);let s=e[t],l=e[t];if(t>-1)if(r==1)s=e[t],l=e[i];else if(r==-1)s=e[i],l=e[t];else for(let c=t;c<=i;c++){let m=e[c];a(m)&&(m<s?s=m:m>l&&(l=m))}return[s??Ee,l??-Ee]}function xs(e,t,i,r){let n=sm(e),o=sm(t);e==t&&(n==-1?(e*=i,t/=i):(e/=i,t*=i));let a=i==10?Rn:b$,s=n==1?Ht:sn,l=o==1?sn:Ht,c=s(a(lt(e))),m=l(a(lt(t))),f=fr(i,c),h=fr(i,m);return i==10&&(c<0&&(f=Ue(f,-c)),m<0&&(h=Ue(h,-m))),r||i==2?(e=f*n,t=h*o):(e=w$(e,f),t=zs(t,h)),[e,t]}function vd(e,t,i,r){let n=xs(e,t,i,r);return e==0&&(n[0]=0),t==0&&(n[1]=0),n}const _d=.1,om={mode:3,pad:_d},Mr={pad:0,soft:null,mode:0},i6={min:Mr,max:Mr};function Sa(e,t,i,r){return Ts(i)?am(e,t,i):(Mr.pad=i,Mr.soft=r?0:null,Mr.mode=r?3:0,am(e,t,i6))}function ye(e,t){return e??t}function r6(e,t,i){for(t=ye(t,0),i=ye(i,e.length-1);t<=i;){if(e[t]!=null)return!0;t++}return!1}function am(e,t,i){let r=i.min,n=i.max,o=ye(r.pad,0),a=ye(n.pad,0),s=ye(r.hard,-Ee),l=ye(n.hard,Ee),c=ye(r.soft,Ee),m=ye(n.soft,-Ee),f=ye(r.mode,0),h=ye(n.mode,0),$=t-e,S=Rn($),U=Ut(lt(e),lt(t)),D=Rn(U),J=lt(D-S);($<1e-24||J>10)&&($=0,(e==0||t==0)&&($=1e-24,f==2&&c!=Ee&&(o=0),h==2&&m!=-Ee&&(a=0)));let k=$||U||1e3,T=Rn(k),z=fr(10,Ht(T)),ee=k*($==0?e==0?.1:1:o),C=Ue(w$(e-ee,z/10),24),se=e>=c&&(f==1||f==3&&C<=c||f==2&&C>=c)?c:Ee,te=Ut(s,C<se&&e>=se?se:yn(se,C)),pe=k*($==0?t==0?.1:1:a),oe=Ue(zs(t+pe,z/10),24),O=t<=m&&(h==1||h==3&&oe>=m||h==2&&oe<=m)?m:-Ee,ue=yn(l,oe>O&&t<=O?O:Ut(O,oe));return te==ue&&te==0&&(ue=100),[te,ue]}const o6=new Intl.NumberFormat(br?QT.language:"en-US"),bd=e=>o6.format(e),Xt=Math,la=Xt.PI,lt=Xt.abs,Ht=Xt.floor,st=Xt.round,sn=Xt.ceil,yn=Xt.min,Ut=Xt.max,fr=Xt.pow,sm=Xt.sign,Rn=Xt.log10,b$=Xt.log2,a6=(e,t=1)=>Xt.sinh(e)*t,sl=(e,t=1)=>Xt.asinh(e/t),Ee=1/0;function lm(e){return(Rn((e^e>>31)-(e>>31))|0)+1}function El(e,t,i){return yn(Ut(e,t),i)}function $$(e){return typeof e=="function"}function he(e){return $$(e)?e:()=>e}const s6=()=>{},y$=e=>e,k$=(e,t)=>t,l6=e=>null,um=e=>!0,cm=(e,t)=>e==t,u6=/\.\d*?(?=9{6,}|0{6,})/gm,Ti=e=>{if(I$(e)||ni.has(e))return e;const t=`${e}`,i=t.match(u6);if(i==null)return e;let r=i[0].length-1;if(t.indexOf("e-")!=-1){let[n,o]=t.split("e");return+`${Ti(n)}e${o}`}return Ue(e,r)};function vi(e,t){return Ti(Ue(Ti(e/t))*t)}function zs(e,t){return Ti(sn(Ti(e/t))*t)}function w$(e,t){return Ti(Ht(Ti(e/t))*t)}function Ue(e,t=0){if(I$(e))return e;let i=10**t,r=e*i*(1+Number.EPSILON);return st(r)/i}const ni=new Map;function S$(e){return((""+e).split(".")[1]||"").length}function eo(e,t,i,r){let n=[],o=r.map(S$);for(let a=t;a<i;a++){let s=lt(a),l=Ue(fr(e,a),s);for(let c=0;c<r.length;c++){let m=e==10?+`${r[c]}e${a}`:r[c]*l,f=(a>=0?0:s)+(a>=o[c]?0:o[c]),h=e==10?m:Ue(m,f);n.push(h),ni.set(h,f)}}return n}const Fr={},$d=[],mr=[null,null],Gn=Array.isArray,I$=Number.isInteger,c6=e=>e===void 0;function dm(e){return typeof e=="string"}function Ts(e){let t=!1;if(e!=null){let i=e.constructor;t=i==null||i==Object}return t}function d6(e){return e!=null&&typeof e=="object"}const f6=Object.getPrototypeOf(Uint8Array),x$="__proto__";function pr(e,t=Ts){let i;if(Gn(e)){let r=e.find(n=>n!=null);if(Gn(r)||t(r)){i=Array(e.length);for(let n=0;n<e.length;n++)i[n]=pr(e[n],t)}else i=e.slice()}else if(e instanceof f6)i=e.slice();else if(t(e)){i={};for(let r in e)r!=x$&&(i[r]=pr(e[r],t))}else i=e;return i}function rt(e){let t=arguments;for(let i=1;i<t.length;i++){let r=t[i];for(let n in r)n!=x$&&(Ts(e[n])?rt(e[n],pr(r[n])):e[n]=pr(r[n]))}return e}const m6=0,p6=1,h6=2;function g6(e,t,i){for(let r=0,n,o=-1;r<t.length;r++){let a=t[r];if(a>o){for(n=a-1;n>=0&&e[n]==null;)e[n--]=null;for(n=a+1;n<i&&e[n]==null;)e[o=n++]=null}}}function v6(e,t){if($6(e)){let a=e[0].slice();for(let s=1;s<e.length;s++)a.push(...e[s].slice(1));return y6(a[0])||(a=b6(a)),a}let i=new Set;for(let a=0;a<e.length;a++){let l=e[a][0],c=l.length;for(let m=0;m<c;m++)i.add(l[m])}let r=[Array.from(i).sort((a,s)=>a-s)],n=r[0].length,o=new Map;for(let a=0;a<n;a++)o.set(r[0][a],a);for(let a=0;a<e.length;a++){let s=e[a],l=s[0];for(let c=1;c<s.length;c++){let m=s[c],f=Array(n).fill(void 0),h=t?t[a][c]:p6,$=[];for(let S=0;S<m.length;S++){let U=m[S],D=o.get(l[S]);U===null?h!=m6&&(f[D]=U,h==h6&&$.push(D)):f[D]=U}g6(f,$,n),r.push(f)}}return r}const _6=typeof queueMicrotask>"u"?e=>Promise.resolve().then(e):queueMicrotask;function b6(e){let t=e[0],i=t.length,r=Array(i);for(let o=0;o<r.length;o++)r[o]=o;r.sort((o,a)=>t[o]-t[a]);let n=[];for(let o=0;o<e.length;o++){let a=e[o],s=Array(i);for(let l=0;l<i;l++)s[l]=a[r[l]];n.push(s)}return n}function $6(e){let t=e[0][0],i=t.length;for(let r=1;r<e.length;r++){let n=e[r][0];if(n.length!=i)return!1;if(n!=t){for(let o=0;o<i;o++)if(n[o]!=t[o])return!1}}return!0}function y6(e,t=100){const i=e.length;if(i<=1)return!0;let r=0,n=i-1;for(;r<=n&&e[r]==null;)r++;for(;n>=r&&e[n]==null;)n--;if(n<=r)return!0;const o=Ut(1,Ht((n-r+1)/t));for(let a=e[r],s=r+o;s<=n;s+=o){const l=e[s];if(l!=null){if(l<=a)return!1;a=l}}return!0}const z$=["January","February","March","April","May","June","July","August","September","October","November","December"],T$=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];function Z$(e){return e.slice(0,3)}const k6=T$.map(Z$),w6=z$.map(Z$),S6={MMMM:z$,MMM:w6,WWWW:T$,WWW:k6};function Or(e){return(e<10?"0":"")+e}function I6(e){return(e<10?"00":e<100?"0":"")+e}const x6={YYYY:e=>e.getFullYear(),YY:e=>(e.getFullYear()+"").slice(2),MMMM:(e,t)=>t.MMMM[e.getMonth()],MMM:(e,t)=>t.MMM[e.getMonth()],MM:e=>Or(e.getMonth()+1),M:e=>e.getMonth()+1,DD:e=>Or(e.getDate()),D:e=>e.getDate(),WWWW:(e,t)=>t.WWWW[e.getDay()],WWW:(e,t)=>t.WWW[e.getDay()],HH:e=>Or(e.getHours()),H:e=>e.getHours(),h:e=>{let t=e.getHours();return t==0?12:t>12?t-12:t},AA:e=>e.getHours()>=12?"PM":"AM",aa:e=>e.getHours()>=12?"pm":"am",a:e=>e.getHours()>=12?"p":"a",mm:e=>Or(e.getMinutes()),m:e=>e.getMinutes(),ss:e=>Or(e.getSeconds()),s:e=>e.getSeconds(),fff:e=>I6(e.getMilliseconds())};function yd(e,t){t=t||S6;let i=[],r=/\{([a-z]+)\}|[^{]+/gi,n;for(;n=r.exec(e);)i.push(n[0][0]=="{"?x6[n[1]]:n[0]);return o=>{let a="";for(let s=0;s<i.length;s++)a+=typeof i[s]=="string"?i[s]:i[s](o,t);return a}}const z6=new Intl.DateTimeFormat().resolvedOptions().timeZone;function T6(e,t){let i;return t=="UTC"||t=="Etc/UTC"?i=new Date(+e+e.getTimezoneOffset()*6e4):t==z6?i=e:(i=new Date(e.toLocaleString("en-US",{timeZone:t})),i.setMilliseconds(e.getMilliseconds())),i}const P$=e=>e%1==0,Ia=[1,2,2.5,5],Z6=eo(10,-32,0,Ia),E$=eo(10,0,32,Ia),P6=E$.filter(P$),_i=Z6.concat(E$),kd=`
141
141
  `,U$="{YYYY}",fm=kd+U$,N$="{M}/{D}",Ar=kd+N$,ia=Ar+"/{YY}",O$="{aa}",E6="{h}:{mm}",tr=E6+O$,mm=kd+tr,pm=":{ss}",Ie=null;function j$(e){let t=e*1e3,i=t*60,r=i*60,n=r*24,o=n*30,a=n*365,l=(e==1?eo(10,0,3,Ia).filter(P$):eo(10,-3,0,Ia)).concat([t,t*5,t*10,t*15,t*30,i,i*5,i*10,i*15,i*30,r,r*2,r*3,r*4,r*6,r*8,r*12,n,n*2,n*3,n*4,n*5,n*6,n*7,n*8,n*9,n*10,n*15,o,o*2,o*3,o*4,o*6,a,a*2,a*5,a*10,a*25,a*50,a*100]);const c=[[a,U$,Ie,Ie,Ie,Ie,Ie,Ie,1],[n*28,"{MMM}",fm,Ie,Ie,Ie,Ie,Ie,1],[n,N$,fm,Ie,Ie,Ie,Ie,Ie,1],[r,"{h}"+O$,ia,Ie,Ar,Ie,Ie,Ie,1],[i,tr,ia,Ie,Ar,Ie,Ie,Ie,1],[t,pm,ia+" "+tr,Ie,Ar+" "+tr,Ie,mm,Ie,1],[e,pm+".{fff}",ia+" "+tr,Ie,Ar+" "+tr,Ie,mm,Ie,1]];function m(f){return(h,$,S,U,D,J)=>{let k=[],T=D>=a,z=D>=o&&D<a,ee=f(S),C=Ue(ee*e,3),se=ll(ee.getFullYear(),T?0:ee.getMonth(),z||T?1:ee.getDate()),te=Ue(se*e,3);if(z||T){let pe=z?D/o:0,oe=T?D/a:0,O=C==te?C:Ue(ll(se.getFullYear()+oe,se.getMonth()+pe,1)*e,3),ue=new Date(st(O/e)),V=ue.getFullYear(),ne=ue.getMonth();for(let K=0;O<=U;K++){let ge=ll(V+oe*K,ne+pe*K,1),G=ge-f(Ue(ge*e,3));O=Ue((+ge+G)*e,3),O<=U&&k.push(O)}}else{let pe=D>=n?n:D,oe=Ht(S)-Ht(C),O=te+oe+zs(C-te,pe);k.push(O);let ue=f(O),V=ue.getHours()+ue.getMinutes()/i+ue.getSeconds()/r,ne=D/r,K=h.axes[$]._space,ge=J/K;for(;O=Ue(O+D,e==1?0:3),!(O>U);)if(ne>1){let G=Ht(Ue(V+ne,6))%24,be=f(O).getHours()-G;be>1&&(be=-1),O-=be*r,V=(V+ne)%24;let ze=k[k.length-1];Ue((O-ze)/D,3)*ge>=.7&&k.push(O)}else k.push(O)}return k}}return[l,c,m]}const[U6,N6,O6]=j$(1),[j6,D6,R6]=j$(.001);eo(2,-53,53,[1]);function hm(e,t){return e.map(i=>i.map((r,n)=>n==0||n==8||r==null?r:t(n==1||i[8]==0?r:i[1]+r)))}function gm(e,t){return(i,r,n,o,a)=>{let s=t.find(S=>a>=S[0])||t[t.length-1],l,c,m,f,h,$;return r.map(S=>{let U=e(S),D=U.getFullYear(),J=U.getMonth(),k=U.getDate(),T=U.getHours(),z=U.getMinutes(),ee=U.getSeconds(),C=D!=l&&s[2]||J!=c&&s[3]||k!=m&&s[4]||T!=f&&s[5]||z!=h&&s[6]||ee!=$&&s[7]||s[1];return l=D,c=J,m=k,f=T,h=z,$=ee,C(U)})}}function A6(e,t){let i=yd(t);return(r,n,o,a,s)=>n.map(l=>i(e(l)))}function ll(e,t,i){return new Date(e,t,i)}function vm(e,t){return t(e)}const C6="{YYYY}-{MM}-{DD} {h}:{mm}{aa}";function _m(e,t){return(i,r,n,o)=>o==null?hd:t(e(r))}function L6(e,t){let i=e.series[t];return i.width?i.stroke(e,t):i.points.width?i.points.stroke(e,t):null}function M6(e,t){return e.series[t].fill(e,t)}const F6={show:!0,live:!0,isolate:!1,mount:s6,markers:{show:!0,width:2,stroke:L6,fill:M6,dash:"solid"},idx:null,idxs:null,values:[]};function J6(e,t){let i=e.cursor.points,r=an(),n=i.size(e,t);Ce(r,Dr,n),Ce(r,Rr,n);let o=n/-2;Ce(r,"marginLeft",o),Ce(r,"marginTop",o);let a=i.width(e,t,n);return a&&Ce(r,"borderWidth",a),r}function q6(e,t){let i=e.series[t].points;return i._fill||i._stroke}function V6(e,t){let i=e.series[t].points;return i._stroke||i._fill}function W6(e,t){return e.series[t].points.size}const ul=[0,0];function H6(e,t,i){return ul[0]=t,ul[1]=i,ul}function ra(e,t,i,r=!0){return n=>{n.button==0&&(!r||n.target==t)&&i(n)}}function cl(e,t,i,r=!0){return n=>{(!r||n.target==t)&&i(n)}}const B6={show:!0,x:!0,y:!0,lock:!1,move:H6,points:{one:!1,show:J6,size:W6,width:0,stroke:V6,fill:q6},bind:{mousedown:ra,mouseup:ra,click:ra,dblclick:ra,mousemove:cl,mouseleave:cl,mouseenter:cl},drag:{setScale:!0,x:!0,y:!1,dist:0,uni:null,click:(e,t)=>{t.stopPropagation(),t.stopImmediatePropagation()},_x:!1,_y:!1},focus:{dist:(e,t,i,r,n)=>r-n,prox:-1,bias:0},hover:{skip:[void 0],prox:null,bias:0},left:-10,top:-10,idx:null,dataIdx:null,idxs:null,event:null},D$={show:!0,stroke:"rgba(0,0,0,0.07)",width:2},wd=rt({},D$,{filter:k$}),R$=rt({},wd,{size:10}),A$=rt({},D$,{show:!1}),Sd='12px system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',C$="bold "+Sd,L$=1.5,bm={show:!0,scale:"x",stroke:pd,space:50,gap:5,alignTo:1,size:50,labelGap:0,labelSize:30,labelFont:C$,side:2,grid:wd,ticks:R$,border:A$,font:Sd,lineGap:L$,rotate:0},G6="Value",K6="Time",$m={show:!0,scale:"x",auto:!1,sorted:1,min:Ee,max:-Ee,idxs:[]};function Y6(e,t,i,r,n){return t.map(o=>o==null?"":bd(o))}function X6(e,t,i,r,n,o,a){let s=[],l=ni.get(n)||0;i=a?i:Ue(zs(i,n),l);for(let c=i;c<=r;c=Ue(c+n,l))s.push(Object.is(c,-0)?0:c);return s}function Ul(e,t,i,r,n,o,a){const s=[],l=e.scales[e.axes[t].scale].log,c=l==10?Rn:b$,m=Ht(c(i));n=fr(l,m),l==10&&(n=_i[$n(n,_i)]);let f=i,h=n*l;l==10&&(h=_i[$n(h,_i)]);do s.push(f),f=f+n,l==10&&!ni.has(f)&&(f=Ue(f,ni.get(n))),f>=h&&(n=f,h=n*l,l==10&&(h=_i[$n(h,_i)]));while(f<=r);return s}function Q6(e,t,i,r,n,o,a){let l=e.scales[e.axes[t].scale].asinh,c=r>l?Ul(e,t,Ut(l,i),r,n):[l],m=r>=0&&i<=0?[0]:[];return(i<-l?Ul(e,t,Ut(l,-r),-i,n):[l]).reverse().map(h=>-h).concat(m,c)}const M$=/./,eZ=/[12357]/,tZ=/[125]/,ym=/1/,Nl=(e,t,i,r)=>e.map((n,o)=>t==4&&n==0||o%r==0&&i.test(n.toExponential()[n<0?1:0])?n:null);function nZ(e,t,i,r,n){let o=e.axes[i],a=o.scale,s=e.scales[a],l=e.valToPos,c=o._space,m=l(10,a),f=l(9,a)-m>=c?M$:l(7,a)-m>=c?eZ:l(5,a)-m>=c?tZ:ym;if(f==ym){let h=lt(l(1,a)-m);if(h<c)return Nl(t.slice().reverse(),s.distr,f,sn(c/h)).reverse()}return Nl(t,s.distr,f,1)}function iZ(e,t,i,r,n){let o=e.axes[i],a=o.scale,s=o._space,l=e.valToPos,c=lt(l(1,a)-l(2,a));return c<s?Nl(t.slice().reverse(),3,M$,sn(s/c)).reverse():t}function rZ(e,t,i,r){return r==null?hd:t==null?"":bd(t)}const km={show:!0,scale:"y",stroke:pd,space:30,gap:5,alignTo:1,size:50,labelGap:0,labelSize:30,labelFont:C$,side:3,grid:wd,ticks:R$,border:A$,font:Sd,lineGap:L$,rotate:0};function oZ(e,t){let i=3+(e||1)*2;return Ue(i*t,3)}function aZ(e,t){let{scale:i,idxs:r}=e.series[0],n=e._data[0],o=e.valToPos(n[r[0]],i,!0),a=e.valToPos(n[r[1]],i,!0),s=lt(a-o),l=e.series[t],c=s/(l.points.space*we);return r[1]-r[0]<=c}const wm={scale:null,auto:!0,sorted:0,min:Ee,max:-Ee},F$=(e,t,i,r,n)=>n,Sm={show:!0,auto:!0,sorted:0,gaps:F$,alpha:1,facets:[rt({},wm,{scale:"x"}),rt({},wm,{scale:"y"})]},Im={scale:"y",auto:!0,sorted:0,show:!0,spanGaps:!1,gaps:F$,alpha:1,points:{show:aZ,filter:null},values:null,min:Ee,max:-Ee,idxs:[],path:null,clip:null};function sZ(e,t,i,r,n){return i/10}const J$={time:UT,auto:!0,distr:1,log:10,asinh:1,min:null,max:null,dir:1,ori:0},lZ=rt({},J$,{time:!1,ori:1}),xm={};function q$(e,t){let i=xm[e];return i||(i={key:e,plots:[],sub(r){i.plots.push(r)},unsub(r){i.plots=i.plots.filter(n=>n!=r)},pub(r,n,o,a,s,l,c){for(let m=0;m<i.plots.length;m++)i.plots[m]!=n&&i.plots[m].pub(r,n,o,a,s,l,c)}},e!=null&&(xm[e]=i)),i}const hr=1,Ol=2;function Ni(e,t,i){const r=e.mode,n=e.series[t],o=r==2?e._data[t]:e._data,a=e.scales,s=e.bbox;let l=o[0],c=r==2?o[1]:o[t],m=r==2?a[n.facets[0].scale]:a[e.series[0].scale],f=r==2?a[n.facets[1].scale]:a[n.scale],h=s.left,$=s.top,S=s.width,U=s.height,D=e.valToPosH,J=e.valToPosV;return m.ori==0?i(n,l,c,m,f,D,J,h,$,S,U,Ps,$r,Us,W$,B$):i(n,l,c,m,f,J,D,$,h,U,S,Es,yr,zd,H$,G$)}function Id(e,t){let i=0,r=0,n=ye(e.bands,$d);for(let o=0;o<n.length;o++){let a=n[o];a.series[0]==t?i=a.dir:a.series[1]==t&&(a.dir==1?r|=1:r|=2)}return[i,r==1?-1:r==2?1:r==3?2:0]}function uZ(e,t,i,r,n){let o=e.mode,a=e.series[t],s=o==2?a.facets[1].scale:a.scale,l=e.scales[s];return n==-1?l.min:n==1?l.max:l.distr==3?l.dir==1?l.min:l.max:0}function An(e,t,i,r,n,o){return Ni(e,t,(a,s,l,c,m,f,h,$,S,U,D)=>{let J=a.pxRound;const k=c.dir*(c.ori==0?1:-1),T=c.ori==0?$r:yr;let z,ee;k==1?(z=i,ee=r):(z=r,ee=i);let C=J(f(s[z],c,U,$)),se=J(h(l[z],m,D,S)),te=J(f(s[ee],c,U,$)),pe=J(h(o==1?m.max:m.min,m,D,S)),oe=new Path2D(n);return T(oe,te,pe),T(oe,C,pe),T(oe,C,se),oe})}function Zs(e,t,i,r,n,o){let a=null;if(e.length>0){a=new Path2D;const s=t==0?Us:zd;let l=i;for(let f=0;f<e.length;f++){let h=e[f];if(h[1]>h[0]){let $=h[0]-l;$>0&&s(a,l,r,$,r+o),l=h[1]}}let c=i+n-l,m=10;c>0&&s(a,l,r-m/2,c,r+o+m)}return a}function cZ(e,t,i){let r=e[e.length-1];r&&r[0]==t?r[1]=i:e.push([t,i])}function xd(e,t,i,r,n,o,a){let s=[],l=e.length;for(let c=n==1?i:r;c>=i&&c<=r;c+=n)if(t[c]===null){let f=c,h=c;if(n==1)for(;++c<=r&&t[c]===null;)h=c;else for(;--c>=i&&t[c]===null;)h=c;let $=o(e[f]),S=h==f?$:o(e[h]),U=f-n;$=a<=0&&U>=0&&U<l?o(e[U]):$;let J=h+n;S=a>=0&&J>=0&&J<l?o(e[J]):S,S>=$&&s.push([$,S])}return s}function zm(e){return e==0?y$:e==1?st:t=>vi(t,e)}function V$(e){let t=e==0?Ps:Es,i=e==0?(n,o,a,s,l,c)=>{n.arcTo(o,a,s,l,c)}:(n,o,a,s,l,c)=>{n.arcTo(a,o,l,s,c)},r=e==0?(n,o,a,s,l)=>{n.rect(o,a,s,l)}:(n,o,a,s,l)=>{n.rect(a,o,l,s)};return(n,o,a,s,l,c=0,m=0)=>{c==0&&m==0?r(n,o,a,s,l):(c=yn(c,s/2,l/2),m=yn(m,s/2,l/2),t(n,o+c,a),i(n,o+s,a,o+s,a+l,c),i(n,o+s,a+l,o,a+l,m),i(n,o,a+l,o,a,m),i(n,o,a,o+s,a,c),n.closePath())}}const Ps=(e,t,i)=>{e.moveTo(t,i)},Es=(e,t,i)=>{e.moveTo(i,t)},$r=(e,t,i)=>{e.lineTo(t,i)},yr=(e,t,i)=>{e.lineTo(i,t)},Us=V$(0),zd=V$(1),W$=(e,t,i,r,n,o)=>{e.arc(t,i,r,n,o)},H$=(e,t,i,r,n,o)=>{e.arc(i,t,r,n,o)},B$=(e,t,i,r,n,o,a)=>{e.bezierCurveTo(t,i,r,n,o,a)},G$=(e,t,i,r,n,o,a)=>{e.bezierCurveTo(i,t,n,r,a,o)};function K$(e){return(t,i,r,n,o)=>Ni(t,i,(a,s,l,c,m,f,h,$,S,U,D)=>{let{pxRound:J,points:k}=a,T,z;c.ori==0?(T=Ps,z=W$):(T=Es,z=H$);const ee=Ue(k.width*we,3);let C=(k.size-k.width)/2*we,se=Ue(C*2,3),te=new Path2D,pe=new Path2D,{left:oe,top:O,width:ue,height:V}=t.bbox;Us(pe,oe-se,O-se,ue+se*2,V+se*2);const ne=K=>{if(l[K]!=null){let ge=J(f(s[K],c,U,$)),G=J(h(l[K],m,D,S));T(te,ge+C,G),z(te,ge,G,C,0,la*2)}};if(o)o.forEach(ne);else for(let K=r;K<=n;K++)ne(K);return{stroke:ee>0?te:null,fill:te,clip:pe,flags:hr|Ol}})}function Y$(e){return(t,i,r,n,o,a)=>{r!=n&&(o!=r&&a!=r&&e(t,i,r),o!=n&&a!=n&&e(t,i,n),e(t,i,a))}}const dZ=Y$($r),fZ=Y$(yr);function X$(e){const t=ye(e?.alignGaps,0);return(i,r,n,o)=>Ni(i,r,(a,s,l,c,m,f,h,$,S,U,D)=>{[n,o]=Is(l,n,o);let J=a.pxRound,k=V=>J(f(V,c,U,$)),T=V=>J(h(V,m,D,S)),z,ee;c.ori==0?(z=$r,ee=dZ):(z=yr,ee=fZ);const C=c.dir*(c.ori==0?1:-1),se={stroke:new Path2D,fill:null,clip:null,band:null,gaps:null,flags:hr},te=se.stroke;let pe=!1;if(o-n>=U*4){let V=W=>i.posToVal(W,c.key,!0),ne=null,K=null,ge,G,Nt,Je=k(s[C==1?n:o]),be=k(s[n]),ze=k(s[o]),de=V(C==1?be+1:ze-1);for(let W=C==1?n:o;W>=n&&W<=o;W+=C){let ot=s[W],qe=(C==1?ot<de:ot>de)?Je:k(ot),Se=l[W];qe==Je?Se!=null?(G=Se,ne==null?(z(te,qe,T(G)),ge=ne=K=G):G<ne?ne=G:G>K&&(K=G)):Se===null&&(pe=!0):(ne!=null&&ee(te,Je,T(ne),T(K),T(ge),T(G)),Se!=null?(G=Se,z(te,qe,T(G)),ne=K=ge=G):(ne=K=null,Se===null&&(pe=!0)),Je=qe,de=V(Je+C))}ne!=null&&ne!=K&&Nt!=Je&&ee(te,Je,T(ne),T(K),T(ge),T(G))}else for(let V=C==1?n:o;V>=n&&V<=o;V+=C){let ne=l[V];ne===null?pe=!0:ne!=null&&z(te,k(s[V]),T(ne))}let[O,ue]=Id(i,r);if(a.fill!=null||O!=0){let V=se.fill=new Path2D(te),ne=a.fillTo(i,r,a.min,a.max,O),K=T(ne),ge=k(s[n]),G=k(s[o]);C==-1&&([G,ge]=[ge,G]),z(V,G,K),z(V,ge,K)}if(!a.spanGaps){let V=[];pe&&V.push(...xd(s,l,n,o,C,k,t)),se.gaps=V=a.gaps(i,r,n,o,V),se.clip=Zs(V,c.ori,$,S,U,D)}return ue!=0&&(se.band=ue==2?[An(i,r,n,o,te,-1),An(i,r,n,o,te,1)]:An(i,r,n,o,te,ue)),se})}function mZ(e){const t=ye(e.align,1),i=ye(e.ascDesc,!1),r=ye(e.alignGaps,0),n=ye(e.extend,!1);return(o,a,s,l)=>Ni(o,a,(c,m,f,h,$,S,U,D,J,k,T)=>{[s,l]=Is(f,s,l);let z=c.pxRound,{left:ee,width:C}=o.bbox,se=be=>z(S(be,h,k,D)),te=be=>z(U(be,$,T,J)),pe=h.ori==0?$r:yr;const oe={stroke:new Path2D,fill:null,clip:null,band:null,gaps:null,flags:hr},O=oe.stroke,ue=h.dir*(h.ori==0?1:-1);let V=te(f[ue==1?s:l]),ne=se(m[ue==1?s:l]),K=ne,ge=ne;n&&t==-1&&(ge=ee,pe(O,ge,V)),pe(O,ne,V);for(let be=ue==1?s:l;be>=s&&be<=l;be+=ue){let ze=f[be];if(ze==null)continue;let de=se(m[be]),W=te(ze);t==1?pe(O,de,V):pe(O,K,W),pe(O,de,W),V=W,K=de}let G=K;n&&t==1&&(G=ee+C,pe(O,G,V));let[Nt,Je]=Id(o,a);if(c.fill!=null||Nt!=0){let be=oe.fill=new Path2D(O),ze=c.fillTo(o,a,c.min,c.max,Nt),de=te(ze);pe(be,G,de),pe(be,ge,de)}if(!c.spanGaps){let be=[];be.push(...xd(m,f,s,l,ue,se,r));let ze=c.width*we/2,de=i||t==1?ze:-ze,W=i||t==-1?-ze:ze;be.forEach(ot=>{ot[0]+=de,ot[1]+=W}),oe.gaps=be=c.gaps(o,a,s,l,be),oe.clip=Zs(be,h.ori,D,J,k,T)}return Je!=0&&(oe.band=Je==2?[An(o,a,s,l,O,-1),An(o,a,s,l,O,1)]:An(o,a,s,l,O,Je)),oe})}function Tm(e,t,i,r,n,o,a=Ee){if(e.length>1){let s=null;for(let l=0,c=1/0;l<e.length;l++)if(t[l]!==void 0){if(s!=null){let m=lt(e[l]-e[s]);m<c&&(c=m,a=lt(i(e[l],r,n,o)-i(e[s],r,n,o)))}s=l}}return a}function pZ(e){e=e||Fr;const t=ye(e.size,[.6,Ee,1]),i=e.align||0,r=e.gap||0;let n=e.radius;n=n==null?[0,0]:typeof n=="number"?[n,0]:n;const o=he(n),a=1-t[0],s=ye(t[1],Ee),l=ye(t[2],1),c=ye(e.disp,Fr),m=ye(e.each,$=>{}),{fill:f,stroke:h}=c;return($,S,U,D)=>Ni($,S,(J,k,T,z,ee,C,se,te,pe,oe,O)=>{let ue=J.pxRound,V=i,ne=r*we,K=s*we,ge=l*we,G,Nt;z.ori==0?[G,Nt]=o($,S):[Nt,G]=o($,S);const Je=z.dir*(z.ori==0?1:-1);let be=z.ori==0?Us:zd,ze=z.ori==0?m:(L,je,at,Ri,ai,Sn,si)=>{m(L,je,at,ai,Ri,si,Sn)},de=ye($.bands,$d).find(L=>L.series[0]==S),W=de!=null?de.dir:0,ot=J.fillTo($,S,J.min,J.max,W),xt=ue(se(ot,ee,O,pe)),qe,Se,pn,Lt=oe,Be=ue(J.width*we),wn=!1,On=null,Qt=null,Jn=null,Oi=null;f!=null&&(Be==0||h!=null)&&(wn=!0,On=f.values($,S,U,D),Qt=new Map,new Set(On).forEach(L=>{L!=null&&Qt.set(L,new Path2D)}),Be>0&&(Jn=h.values($,S,U,D),Oi=new Map,new Set(Jn).forEach(L=>{L!=null&&Oi.set(L,new Path2D)})));let{x0:ji,size:wr}=c;if(ji!=null&&wr!=null){V=1,k=ji.values($,S,U,D),ji.unit==2&&(k=k.map(at=>$.posToVal(te+at*oe,z.key,!0)));let L=wr.values($,S,U,D);wr.unit==2?Se=L[0]*oe:Se=C(L[0],z,oe,te)-C(0,z,oe,te),Lt=Tm(k,T,C,z,oe,te,Lt),pn=Lt-Se+ne}else Lt=Tm(k,T,C,z,oe,te,Lt),pn=Lt*a+ne,Se=Lt-pn;pn<1&&(pn=0),Be>=Se/2&&(Be=0),pn<5&&(ue=y$);let Ro=pn>0,ri=Lt-pn-(Ro?Be:0);Se=ue(El(ri,ge,K)),qe=(V==0?Se/2:V==Je?0:Se)-V*Je*((V==0?ne/2:0)+(Ro?Be/2:0));const zt={stroke:null,fill:null,clip:null,band:null,gaps:null,flags:0},Di=wn?null:new Path2D;let jn=null;if(de!=null)jn=$.data[de.series[1]];else{let{y0:L,y1:je}=c;L!=null&&je!=null&&(T=je.values($,S,U,D),jn=L.values($,S,U,D))}let oi=G*Se,le=Nt*Se;for(let L=Je==1?U:D;L>=U&&L<=D;L+=Je){let je=T[L];if(je==null)continue;if(jn!=null){let Ot=jn[L]??0;if(je-Ot==0)continue;xt=se(Ot,ee,O,pe)}let at=z.distr!=2||c!=null?k[L]:L,Ri=C(at,z,oe,te),ai=se(ye(je,ot),ee,O,pe),Sn=ue(Ri-qe),si=ue(Ut(ai,xt)),Mt=ue(yn(ai,xt)),en=si-Mt;if(je!=null){let Ot=je<0?le:oi,hn=je<0?oi:le;wn?(Be>0&&Jn[L]!=null&&be(Oi.get(Jn[L]),Sn,Mt+Ht(Be/2),Se,Ut(0,en-Be),Ot,hn),On[L]!=null&&be(Qt.get(On[L]),Sn,Mt+Ht(Be/2),Se,Ut(0,en-Be),Ot,hn)):be(Di,Sn,Mt+Ht(Be/2),Se,Ut(0,en-Be),Ot,hn),ze($,S,L,Sn-Be/2,Mt,Se+Be,en)}}return Be>0?zt.stroke=wn?Oi:Di:wn||(zt._fill=J.width==0?J._fill:J._stroke??J._fill,zt.width=0),zt.fill=wn?Qt:Di,zt})}function hZ(e,t){const i=ye(t?.alignGaps,0);return(r,n,o,a)=>Ni(r,n,(s,l,c,m,f,h,$,S,U,D,J)=>{[o,a]=Is(c,o,a);let k=s.pxRound,T=G=>k(h(G,m,D,S)),z=G=>k($(G,f,J,U)),ee,C,se;m.ori==0?(ee=Ps,se=$r,C=B$):(ee=Es,se=yr,C=G$);const te=m.dir*(m.ori==0?1:-1);let pe=T(l[te==1?o:a]),oe=pe,O=[],ue=[];for(let G=te==1?o:a;G>=o&&G<=a;G+=te)if(c[G]!=null){let Je=l[G],be=T(Je);O.push(oe=be),ue.push(z(c[G]))}const V={stroke:e(O,ue,ee,se,C,k),fill:null,clip:null,band:null,gaps:null,flags:hr},ne=V.stroke;let[K,ge]=Id(r,n);if(s.fill!=null||K!=0){let G=V.fill=new Path2D(ne),Nt=s.fillTo(r,n,s.min,s.max,K),Je=z(Nt);se(G,oe,Je),se(G,pe,Je)}if(!s.spanGaps){let G=[];G.push(...xd(l,c,o,a,te,T,i)),V.gaps=G=s.gaps(r,n,o,a,G),V.clip=Zs(G,m.ori,S,U,D,J)}return ge!=0&&(V.band=ge==2?[An(r,n,o,a,ne,-1),An(r,n,o,a,ne,1)]:An(r,n,o,a,ne,ge)),V})}function gZ(e){return hZ(vZ,e)}function vZ(e,t,i,r,n,o){const a=e.length;if(a<2)return null;const s=new Path2D;if(i(s,e[0],t[0]),a==2)r(s,e[1],t[1]);else{let l=Array(a),c=Array(a-1),m=Array(a-1),f=Array(a-1);for(let h=0;h<a-1;h++)m[h]=t[h+1]-t[h],f[h]=e[h+1]-e[h],c[h]=m[h]/f[h];l[0]=c[0];for(let h=1;h<a-1;h++)c[h]===0||c[h-1]===0||c[h-1]>0!=c[h]>0?l[h]=0:(l[h]=3*(f[h-1]+f[h])/((2*f[h]+f[h-1])/c[h-1]+(f[h]+2*f[h-1])/c[h]),isFinite(l[h])||(l[h]=0));l[a-1]=c[a-2];for(let h=0;h<a-1;h++)n(s,e[h]+f[h]/3,t[h]+l[h]*f[h]/3,e[h+1]-f[h]/3,t[h+1]-l[h+1]*f[h]/3,e[h+1],t[h+1])}return s}const jl=new Set;function Zm(){for(let e of jl)e.syncRect(!0)}br&&(wi(YT,ir,Zm),wi(XT,ir,Zm,!0),wi(wa,ir,()=>{St.pxRatio=we}));const _Z=X$(),bZ=K$();function Pm(e,t,i,r){return(r?[e[0],e[1]].concat(e.slice(2)):[e[0]].concat(e.slice(1))).map((o,a)=>Dl(o,a,t,i))}function $Z(e,t){return e.map((i,r)=>r==0?{}:rt({},t,i))}function Dl(e,t,i,r){return rt({},t==0?i:r,e)}function Q$(e,t,i){return t==null?mr:[t,i]}const yZ=Q$;function kZ(e,t,i){return t==null?mr:Sa(t,i,_d,!0)}function ey(e,t,i,r){return t==null?mr:xs(t,i,e.scales[r].log,!1)}const wZ=ey;function ty(e,t,i,r){return t==null?mr:vd(t,i,e.scales[r].log,!1)}const SZ=ty;function IZ(e,t,i,r,n){let o=Ut(lm(e),lm(t)),a=t-e,s=$n(n/r*a,i);do{let l=i[s],c=r*l/a;if(c>=n&&o+(l<5?ni.get(l):0)<=17)return[l,c]}while(++s<i.length);return[0,0]}function Em(e){let t,i;return e=e.replace(/(\d+)px/,(r,n)=>(t=st((i=+n)*we))+"px"),[e,t,i]}function xZ(e){e.show&&[e.font,e.labelFont].forEach(t=>{let i=Ue(t[2]*we,1);t[0]=t[0].replace(/[0-9.]+px/,i+"px"),t[1]=i})}function St(e,t,i){const r={mode:ye(e.mode,1)},n=r.mode;function o(u,d,p,g){let _=d.valToPct(u);return g+p*(d.dir==-1?1-_:_)}function a(u,d,p,g){let _=d.valToPct(u);return g+p*(d.dir==-1?_:1-_)}function s(u,d,p,g){return d.ori==0?o(u,d,p,g):a(u,d,p,g)}r.valToPosH=o,r.valToPosV=a;let l=!1;r.status=0;const c=r.root=an(NT);if(e.id!=null&&(c.id=e.id),Vt(c,e.class),e.title){let u=an(DT,c);u.textContent=e.title}const m=bn("canvas"),f=r.ctx=m.getContext("2d"),h=an(RT,c);wi("click",h,u=>{u.target===S&&(De!=Bi||Ve!=Gi)&&vt.click(r,u)},!0);const $=r.under=an(AT,h);h.appendChild(m);const S=r.over=an(CT,h);e=pr(e);const U=+ye(e.pxAlign,1),D=zm(U);(e.plugins||[]).forEach(u=>{u.opts&&(e=u.opts(r,e)||e)});const J=e.ms||.001,k=r.series=n==1?Pm(e.series||[],$m,Im,!1):$Z(e.series||[null],Sm),T=r.axes=Pm(e.axes||[],bm,km,!0),z=r.scales={},ee=r.bands=e.bands||[];ee.forEach(u=>{u.fill=he(u.fill||null),u.dir=ye(u.dir,-1)});const C=n==2?k[1].facets[0].scale:k[0].scale,se={axes:Ty,series:wy},te=(e.drawOrder||["axes","series"]).map(u=>se[u]);function pe(u){const d=u.distr==3?p=>Rn(p>0?p:u.clamp(r,p,u.min,u.max,u.key)):u.distr==4?p=>sl(p,u.asinh):u.distr==100?p=>u.fwd(p):p=>p;return p=>{let g=d(p),{_min:_,_max:y}=u,x=y-_;return(g-_)/x}}function oe(u){let d=z[u];if(d==null){let p=(e.scales||Fr)[u]||Fr;if(p.from!=null){oe(p.from);let g=rt({},z[p.from],p,{key:u});g.valToPct=pe(g),z[u]=g}else{d=z[u]=rt({},u==C?J$:lZ,p),d.key=u;let g=d.time,_=d.range,y=Gn(_);if((u!=C||n==2&&!g)&&(y&&(_[0]==null||_[1]==null)&&(_={min:_[0]==null?om:{mode:1,hard:_[0],soft:_[0]},max:_[1]==null?om:{mode:1,hard:_[1],soft:_[1]}},y=!1),!y&&Ts(_))){let x=_;_=(Z,N,R)=>N==null?mr:Sa(N,R,x)}d.range=he(_||(g?yZ:u==C?d.distr==3?wZ:d.distr==4?SZ:Q$:d.distr==3?ey:d.distr==4?ty:kZ)),d.auto=he(y?!1:d.auto),d.clamp=he(d.clamp||sZ),d._min=d._max=null,d.valToPct=pe(d)}}}oe("x"),oe("y"),n==1&&k.forEach(u=>{oe(u.scale)}),T.forEach(u=>{oe(u.scale)});for(let u in e.scales)oe(u);const O=z[C],ue=O.distr;let V,ne;O.ori==0?(Vt(c,OT),V=o,ne=a):(Vt(c,jT),V=a,ne=o);const K={};for(let u in z){let d=z[u];(d.min!=null||d.max!=null)&&(K[u]={min:d.min,max:d.max},d.min=d.max=null)}const ge=e.tzDate||(u=>new Date(st(u/J))),G=e.fmtDate||yd,Nt=J==1?O6(ge):R6(ge),Je=gm(ge,hm(J==1?N6:D6,G)),be=_m(ge,vm(C6,G)),ze=[],de=r.legend=rt({},F6,e.legend),W=r.cursor=rt({},B6,{drag:{y:n==2}},e.cursor),ot=de.show,xt=W.show,qe=de.markers;de.idxs=ze,qe.width=he(qe.width),qe.dash=he(qe.dash),qe.stroke=he(qe.stroke),qe.fill=he(qe.fill);let Se,pn,Lt,Be=[],wn=[],On,Qt=!1,Jn={};if(de.live){const u=k[1]?k[1].values:null;Qt=u!=null,On=Qt?u(r,1,0):{_:0};for(let d in On)Jn[d]=hd}if(ot)if(Se=bn("table",VT,c),Lt=bn("tbody",null,Se),de.mount(r,Se),Qt){pn=bn("thead",null,Se,Lt);let u=bn("tr",null,pn);bn("th",null,u);for(var Oi in On)bn("th",Wf,u).textContent=Oi}else Vt(Se,HT),de.live&&Vt(Se,WT);const ji={show:!0},wr={show:!1};function Ro(u,d){if(d==0&&(Qt||!de.live||n==2))return mr;let p=[],g=bn("tr",BT,Lt,Lt.childNodes[d]);Vt(g,u.class),u.show||Vt(g,bi);let _=bn("th",null,g);if(qe.show){let Z=an(GT,_);if(d>0){let N=qe.width(r,d);N&&(Z.style.border=N+"px "+qe.dash(r,d)+" "+qe.stroke(r,d)),Z.style.background=qe.fill(r,d)}}let y=an(Wf,_);u.label instanceof HTMLElement?y.appendChild(u.label):y.textContent=u.label,d>0&&(qe.show||(y.style.color=u.width>0?qe.stroke(r,d):qe.fill(r,d)),zt("click",_,Z=>{if(W._lock)return;ui(Z);let N=k.indexOf(u);if((Z.ctrlKey||Z.metaKey)!=de.isolate){let R=k.some((A,F)=>F>0&&F!=N&&A.show);k.forEach((A,F)=>{F>0&&xn(F,R?F==N?ji:wr:ji,!0,it.setSeries)})}else xn(N,{show:!u.show},!0,it.setSeries)},!1),Ci&&zt(Kf,_,Z=>{W._lock||(ui(Z),xn(k.indexOf(u),Yi,!0,it.setSeries))},!1));for(var x in On){let Z=bn("td",KT,g);Z.textContent="--",p.push(Z)}return[g,p]}const ri=new Map;function zt(u,d,p,g=!0){const _=ri.get(d)||{},y=W.bind[u](r,d,p,g);y&&(wi(u,d,_[u]=y),ri.set(d,_))}function Di(u,d,p){const g=ri.get(d)||{};for(let _ in g)(u==null||_==u)&&(Pl(_,d,g[_]),delete g[_]);u==null&&ri.delete(d)}let jn=0,oi=0,le=0,L=0,je=0,at=0,Ri=je,ai=at,Sn=le,si=L,Mt=0,en=0,Ot=0,hn=0;r.bbox={};let Ns=!1,Ao=!1,Ai=!1,li=!1,Co=!1,tn=!1;function Os(u,d,p){(p||u!=r.width||d!=r.height)&&Pd(u,d),qi(!1),Ai=!0,Ao=!0,Vi()}function Pd(u,d){r.width=jn=le=u,r.height=oi=L=d,je=at=0,gy(),vy();let p=r.bbox;Mt=p.left=vi(je*we,.5),en=p.top=vi(at*we,.5),Ot=p.width=vi(le*we,.5),hn=p.height=vi(L*we,.5)}const my=3;function py(){let u=!1,d=0;for(;!u;){d++;let p=xy(d),g=zy(d);u=d==my||p&&g,u||(Pd(r.width,r.height),Ao=!0)}}function hy({width:u,height:d}){Os(u,d)}r.setSize=hy;function gy(){let u=!1,d=!1,p=!1,g=!1;T.forEach((_,y)=>{if(_.show&&_._show){let{side:x,_size:Z}=_,N=x%2,R=_.label!=null?_.labelSize:0,A=Z+R;A>0&&(N?(le-=A,x==3?(je+=A,g=!0):p=!0):(L-=A,x==0?(at+=A,u=!0):d=!0))}}),ci[0]=u,ci[1]=p,ci[2]=d,ci[3]=g,le-=qn[1]+qn[3],je+=qn[3],L-=qn[2]+qn[0],at+=qn[0]}function vy(){let u=je+le,d=at+L,p=je,g=at;function _(y,x){switch(y){case 1:return u+=x,u-x;case 2:return d+=x,d-x;case 3:return p-=x,p+x;case 0:return g-=x,g+x}}T.forEach((y,x)=>{if(y.show&&y._show){let Z=y.side;y._pos=_(Z,y._size),y.label!=null&&(y._lpos=_(Z,y.labelSize))}})}if(W.dataIdx==null){let u=W.hover,d=u.skip=new Set(u.skip??[]);d.add(void 0);let p=u.prox=he(u.prox),g=u.bias??=0;W.dataIdx=(_,y,x,Z)=>{if(y==0)return x;let N=x,R=p(_,y,x,Z)??Ee,A=R>=0&&R<Ee,F=O.ori==0?le:L,ie=W.left,ke=t[0],$e=t[y];if(d.has($e[x])){N=null;let fe=null,X=null,H;if(g==0||g==-1)for(H=x;fe==null&&H-- >0;)d.has($e[H])||(fe=H);if(g==0||g==1)for(H=x;X==null&&H++<$e.length;)d.has($e[H])||(X=H);if(fe!=null||X!=null)if(A){let Ae=fe==null?-1/0:V(ke[fe],O,F,0),Qe=X==null?1/0:V(ke[X],O,F,0),ht=ie-Ae,Te=Qe-ie;ht<=Te?ht<=R&&(N=fe):Te<=R&&(N=X)}else N=X==null?fe:fe==null?X:x-fe<=X-x?fe:X}else A&&lt(ie-V(ke[x],O,F,0))>R&&(N=null);return N}}const ui=u=>{W.event=u};W.idxs=ze,W._lock=!1;let kt=W.points;kt.show=he(kt.show),kt.size=he(kt.size),kt.stroke=he(kt.stroke),kt.width=he(kt.width),kt.fill=he(kt.fill);const In=r.focus=rt({},e.focus||{alpha:.3},W.focus),Ci=In.prox>=0,Li=Ci&&kt.one;let nn=[],Mi=[],Fi=[];function Ed(u,d){let p=kt.show(r,d);if(p instanceof HTMLElement)return Vt(p,qT),Vt(p,u.class),Pn(p,-10,-10,le,L),S.insertBefore(p,nn[d]),p}function Ud(u,d){if(n==1||d>0){let p=n==1&&z[u.scale].time,g=u.value;u.value=p?dm(g)?_m(ge,vm(g,G)):g||be:g||rZ,u.label=u.label||(p?K6:G6)}if(Li||d>0){u.width=u.width==null?1:u.width,u.paths=u.paths||_Z||l6,u.fillTo=he(u.fillTo||uZ),u.pxAlign=+ye(u.pxAlign,U),u.pxRound=zm(u.pxAlign),u.stroke=he(u.stroke||null),u.fill=he(u.fill||null),u._stroke=u._fill=u._paths=u._focus=null;let p=oZ(Ut(1,u.width),1),g=u.points=rt({},{size:p,width:Ut(1,p*.2),stroke:u.stroke,space:p*2,paths:bZ,_stroke:null,_fill:null},u.points);g.show=he(g.show),g.filter=he(g.filter),g.fill=he(g.fill),g.stroke=he(g.stroke),g.paths=he(g.paths),g.pxAlign=u.pxAlign}if(ot){let p=Ro(u,d);Be.splice(d,0,p[0]),wn.splice(d,0,p[1]),de.values.push(null)}if(xt){ze.splice(d,0,null);let p=null;Li?d==0&&(p=Ed(u,d)):d>0&&(p=Ed(u,d)),nn.splice(d,0,p),Mi.splice(d,0,0),Fi.splice(d,0,0)}pt("addSeries",d)}function _y(u,d){d=d??k.length,u=n==1?Dl(u,d,$m,Im):Dl(u,d,{},Sm),k.splice(d,0,u),Ud(k[d],d)}r.addSeries=_y;function by(u){if(k.splice(u,1),ot){de.values.splice(u,1),wn.splice(u,1);let d=Be.splice(u,1)[0];Di(null,d.firstChild),d.remove()}xt&&(ze.splice(u,1),nn.splice(u,1)[0].remove(),Mi.splice(u,1),Fi.splice(u,1)),pt("delSeries",u)}r.delSeries=by;const ci=[!1,!1,!1,!1];function $y(u,d){if(u._show=u.show,u.show){let p=u.side%2,g=z[u.scale];g==null&&(u.scale=p?k[1].scale:C,g=z[u.scale]);let _=g.time;u.size=he(u.size),u.space=he(u.space),u.rotate=he(u.rotate),Gn(u.incrs)&&u.incrs.forEach(x=>{!ni.has(x)&&ni.set(x,S$(x))}),u.incrs=he(u.incrs||(g.distr==2?P6:_?J==1?U6:j6:_i)),u.splits=he(u.splits||(_&&g.distr==1?Nt:g.distr==3?Ul:g.distr==4?Q6:X6)),u.stroke=he(u.stroke),u.grid.stroke=he(u.grid.stroke),u.ticks.stroke=he(u.ticks.stroke),u.border.stroke=he(u.border.stroke);let y=u.values;u.values=Gn(y)&&!Gn(y[0])?he(y):_?Gn(y)?gm(ge,hm(y,G)):dm(y)?A6(ge,y):y||Je:y||Y6,u.filter=he(u.filter||(g.distr>=3&&g.log==10?nZ:g.distr==3&&g.log==2?iZ:k$)),u.font=Em(u.font),u.labelFont=Em(u.labelFont),u._size=u.size(r,null,d,0),u._space=u._rotate=u._incrs=u._found=u._splits=u._values=null,u._size>0&&(ci[d]=!0,u._el=an(LT,h))}}function Sr(u,d,p,g){let[_,y,x,Z]=p,N=d%2,R=0;return N==0&&(Z||y)&&(R=d==0&&!_||d==2&&!x?st(bm.size/3):0),N==1&&(_||x)&&(R=d==1&&!y||d==3&&!Z?st(km.size/2):0),R}const Nd=r.padding=(e.padding||[Sr,Sr,Sr,Sr]).map(u=>he(ye(u,Sr))),qn=r._padding=Nd.map((u,d)=>u(r,d,ci,0));let gt,ct=null,dt=null;const Lo=n==1?k[0].idxs:null;let gn=null,Ir=!1;function Od(u,d){if(t=u??[],r.data=r._data=t,n==2){gt=0;for(let p=1;p<k.length;p++)gt+=t[p][0].length}else{t.length==0&&(r.data=r._data=t=[[]]),gn=t[0],gt=gn.length;let p=t;if(ue==2){p=t.slice();let g=p[0]=Array(gt);for(let _=0;_<gt;_++)g[_]=_}r._data=t=p}if(qi(!0),pt("setData"),ue==2&&(Ai=!0),d!==!1){let p=O;p.auto(r,Ir)?js():Wn(C,p.min,p.max),li=li||W.left>=0,tn=!0,Vi()}}r.setData=Od;function js(){Ir=!0;let u,d;n==1&&(gt>0?(ct=Lo[0]=0,dt=Lo[1]=gt-1,u=t[0][ct],d=t[0][dt],ue==2?(u=ct,d=dt):u==d&&(ue==3?[u,d]=xs(u,u,O.log,!1):ue==4?[u,d]=vd(u,u,O.log,!1):O.time?d=u+st(86400/J):[u,d]=Sa(u,d,_d,!0))):(ct=Lo[0]=u=null,dt=Lo[1]=d=null)),Wn(C,u,d)}let Mo,Ji,Ds,Rs,As,Cs,Ls,Ms,Fs,jt;function jd(u,d,p,g,_,y){u??=Bf,p??=$d,g??="butt",_??=Bf,y??="round",u!=Mo&&(f.strokeStyle=Mo=u),_!=Ji&&(f.fillStyle=Ji=_),d!=Ds&&(f.lineWidth=Ds=d),y!=As&&(f.lineJoin=As=y),g!=Cs&&(f.lineCap=Cs=g),p!=Rs&&f.setLineDash(Rs=p)}function Dd(u,d,p,g){d!=Ji&&(f.fillStyle=Ji=d),u!=Ls&&(f.font=Ls=u),p!=Ms&&(f.textAlign=Ms=p),g!=Fs&&(f.textBaseline=Fs=g)}function Js(u,d,p,g,_=0){if(g.length>0&&u.auto(r,Ir)&&(d==null||d.min==null)){let y=ye(ct,0),x=ye(dt,g.length-1),Z=p.min==null?n6(g,y,x,_,u.distr==3):[p.min,p.max];u.min=yn(u.min,p.min=Z[0]),u.max=Ut(u.max,p.max=Z[1])}}const Rd={min:null,max:null};function yy(){for(let g in z){let _=z[g];K[g]==null&&(_.min==null||K[C]!=null&&_.auto(r,Ir))&&(K[g]=Rd)}for(let g in z){let _=z[g];K[g]==null&&_.from!=null&&K[_.from]!=null&&(K[g]=Rd)}K[C]!=null&&qi(!0);let u={};for(let g in K){let _=K[g];if(_!=null){let y=u[g]=pr(z[g],d6);if(_.min!=null)rt(y,_);else if(g!=C||n==2)if(gt==0&&y.from==null){let x=y.range(r,null,null,g);y.min=x[0],y.max=x[1]}else y.min=Ee,y.max=-Ee}}if(gt>0){k.forEach((g,_)=>{if(n==1){let y=g.scale,x=K[y];if(x==null)return;let Z=u[y];if(_==0){let N=Z.range(r,Z.min,Z.max,y);Z.min=N[0],Z.max=N[1],ct=$n(Z.min,t[0]),dt=$n(Z.max,t[0]),dt-ct>1&&(t[0][ct]<Z.min&&ct++,t[0][dt]>Z.max&&dt--),g.min=gn[ct],g.max=gn[dt]}else g.show&&g.auto&&Js(Z,x,g,t[_],g.sorted);g.idxs[0]=ct,g.idxs[1]=dt}else if(_>0&&g.show&&g.auto){let[y,x]=g.facets,Z=y.scale,N=x.scale,[R,A]=t[_],F=u[Z],ie=u[N];F!=null&&Js(F,K[Z],y,R,y.sorted),ie!=null&&Js(ie,K[N],x,A,x.sorted),g.min=x.min,g.max=x.max}});for(let g in u){let _=u[g],y=K[g];if(_.from==null&&(y==null||y.min==null)){let x=_.range(r,_.min==Ee?null:_.min,_.max==-Ee?null:_.max,g);_.min=x[0],_.max=x[1]}}}for(let g in u){let _=u[g];if(_.from!=null){let y=u[_.from];if(y.min==null)_.min=_.max=null;else{let x=_.range(r,y.min,y.max,g);_.min=x[0],_.max=x[1]}}}let d={},p=!1;for(let g in u){let _=u[g],y=z[g];if(y.min!=_.min||y.max!=_.max){y.min=_.min,y.max=_.max;let x=y.distr;y._min=x==3?Rn(y.min):x==4?sl(y.min,y.asinh):x==100?y.fwd(y.min):y.min,y._max=x==3?Rn(y.max):x==4?sl(y.max,y.asinh):x==100?y.fwd(y.max):y.max,d[g]=p=!0}}if(p){k.forEach((g,_)=>{n==2?_>0&&d.y&&(g._paths=null):d[g.scale]&&(g._paths=null)});for(let g in d)Ai=!0,pt("setScale",g);xt&&W.left>=0&&(li=tn=!0)}for(let g in K)K[g]=null}function ky(u){let d=El(ct-1,0,gt-1),p=El(dt+1,0,gt-1);for(;u[d]==null&&d>0;)d--;for(;u[p]==null&&p<gt-1;)p++;return[d,p]}function wy(){if(gt>0){let u=k.some(d=>d._focus)&&jt!=In.alpha;u&&(f.globalAlpha=jt=In.alpha),k.forEach((d,p)=>{if(p>0&&d.show&&(Ad(p,!1),Ad(p,!0),d._paths==null)){let g=jt;jt!=d.alpha&&(f.globalAlpha=jt=d.alpha);let _=n==2?[0,t[p][0].length-1]:ky(t[p]);d._paths=d.paths(r,p,_[0],_[1]),jt!=g&&(f.globalAlpha=jt=g)}}),k.forEach((d,p)=>{if(p>0&&d.show){let g=jt;jt!=d.alpha&&(f.globalAlpha=jt=d.alpha),d._paths!=null&&Cd(p,!1);{let _=d._paths!=null?d._paths.gaps:null,y=d.points.show(r,p,ct,dt,_),x=d.points.filter(r,p,y,_);(y||x)&&(d.points._paths=d.points.paths(r,p,ct,dt,x),Cd(p,!0))}jt!=g&&(f.globalAlpha=jt=g),pt("drawSeries",p)}}),u&&(f.globalAlpha=jt=1)}}function Ad(u,d){let p=d?k[u].points:k[u];p._stroke=p.stroke(r,u),p._fill=p.fill(r,u)}function Cd(u,d){let p=d?k[u].points:k[u],{stroke:g,fill:_,clip:y,flags:x,_stroke:Z=p._stroke,_fill:N=p._fill,_width:R=p.width}=p._paths;R=Ue(R*we,3);let A=null,F=R%2/2;d&&N==null&&(N=R>0?"#fff":Z);let ie=p.pxAlign==1&&F>0;if(ie&&f.translate(F,F),!d){let ke=Mt-R/2,$e=en-R/2,fe=Ot+R,X=hn+R;A=new Path2D,A.rect(ke,$e,fe,X)}d?qs(Z,R,p.dash,p.cap,N,g,_,x,y):Sy(u,Z,R,p.dash,p.cap,N,g,_,x,A,y),ie&&f.translate(-F,-F)}function Sy(u,d,p,g,_,y,x,Z,N,R,A){let F=!1;N!=0&&ee.forEach((ie,ke)=>{if(ie.series[0]==u){let $e=k[ie.series[1]],fe=t[ie.series[1]],X=($e._paths||Fr).band;Gn(X)&&(X=ie.dir==1?X[0]:X[1]);let H,Ae=null;$e.show&&X&&r6(fe,ct,dt)?(Ae=ie.fill(r,ke)||y,H=$e._paths.clip):X=null,qs(d,p,g,_,Ae,x,Z,N,R,A,H,X),F=!0}}),F||qs(d,p,g,_,y,x,Z,N,R,A)}const Ld=hr|Ol;function qs(u,d,p,g,_,y,x,Z,N,R,A,F){jd(u,d,p,g,_),(N||R||F)&&(f.save(),N&&f.clip(N),R&&f.clip(R)),F?(Z&Ld)==Ld?(f.clip(F),A&&f.clip(A),Jo(_,x),Fo(u,y,d)):Z&Ol?(Jo(_,x),f.clip(F),Fo(u,y,d)):Z&hr&&(f.save(),f.clip(F),A&&f.clip(A),Jo(_,x),f.restore(),Fo(u,y,d)):(Jo(_,x),Fo(u,y,d)),(N||R||F)&&f.restore()}function Fo(u,d,p){p>0&&(d instanceof Map?d.forEach((g,_)=>{f.strokeStyle=Mo=_,f.stroke(g)}):d!=null&&u&&f.stroke(d))}function Jo(u,d){d instanceof Map?d.forEach((p,g)=>{f.fillStyle=Ji=g,f.fill(p)}):d!=null&&u&&f.fill(d)}function Iy(u,d,p,g){let _=T[u],y;if(g<=0)y=[0,0];else{let x=_._space=_.space(r,u,d,p,g),Z=_._incrs=_.incrs(r,u,d,p,g,x);y=IZ(d,p,Z,g,x)}return _._found=y}function Vs(u,d,p,g,_,y,x,Z,N,R){let A=x%2/2;U==1&&f.translate(A,A),jd(Z,x,N,R,Z),f.beginPath();let F,ie,ke,$e,fe=_+(g==0||g==3?-y:y);p==0?(ie=_,$e=fe):(F=_,ke=fe);for(let X=0;X<u.length;X++)d[X]!=null&&(p==0?F=ke=u[X]:ie=$e=u[X],f.moveTo(F,ie),f.lineTo(ke,$e));f.stroke(),U==1&&f.translate(-A,-A)}function xy(u){let d=!0;return T.forEach((p,g)=>{if(!p.show)return;let _=z[p.scale];if(_.min==null){p._show&&(d=!1,p._show=!1,qi(!1));return}else p._show||(d=!1,p._show=!0,qi(!1));let y=p.side,x=y%2,{min:Z,max:N}=_,[R,A]=Iy(g,Z,N,x==0?le:L);if(A==0)return;let F=_.distr==2,ie=p._splits=p.splits(r,g,Z,N,R,A,F),ke=_.distr==2?ie.map(H=>gn[H]):ie,$e=_.distr==2?gn[ie[1]]-gn[ie[0]]:R,fe=p._values=p.values(r,p.filter(r,ke,g,A,$e),g,A,$e);p._rotate=y==2?p.rotate(r,fe,g,A):0;let X=p._size;p._size=sn(p.size(r,fe,g,u)),X!=null&&p._size!=X&&(d=!1)}),d}function zy(u){let d=!0;return Nd.forEach((p,g)=>{let _=p(r,g,ci,u);_!=qn[g]&&(d=!1),qn[g]=_}),d}function Ty(){for(let u=0;u<T.length;u++){let d=T[u];if(!d.show||!d._show)continue;let p=d.side,g=p%2,_,y,x=d.stroke(r,u),Z=p==0||p==3?-1:1,[N,R]=d._found;if(d.label!=null){let Zt=d.labelGap*Z,qt=st((d._lpos+Zt)*we);Dd(d.labelFont[0],x,"center",p==2?Nr:Hf),f.save(),g==1?(_=y=0,f.translate(qt,st(en+hn/2)),f.rotate((p==3?-la:la)/2)):(_=st(Mt+Ot/2),y=qt);let mi=$$(d.label)?d.label(r,u,N,R):d.label;f.fillText(mi,_,y),f.restore()}if(R==0)continue;let A=z[d.scale],F=g==0?Ot:hn,ie=g==0?Mt:en,ke=d._splits,$e=A.distr==2?ke.map(Zt=>gn[Zt]):ke,fe=A.distr==2?gn[ke[1]]-gn[ke[0]]:N,X=d.ticks,H=d.border,Ae=X.show?X.size:0,Qe=st(Ae*we),ht=st((d.alignTo==2?d._size-Ae-d.gap:d.gap)*we),Te=d._rotate*-la/180,et=D(d._pos*we),Ft=(Qe+ht)*Z,Tt=et+Ft;y=g==0?Tt:0,_=g==1?Tt:0;let rn=d.font[0],vn=d.align==1?er:d.align==2?rl:Te>0?er:Te<0?rl:g==0?"center":p==3?rl:er,Tn=Te||g==1?"middle":p==2?Nr:Hf;Dd(rn,x,vn,Tn);let Jt=d.font[1]*d.lineGap,on=ke.map(Zt=>D(s(Zt,A,F,ie))),_n=d._values;for(let Zt=0;Zt<_n.length;Zt++){let qt=_n[Zt];if(qt!=null){g==0?_=on[Zt]:y=on[Zt],qt=""+qt;let mi=qt.indexOf(`
142
- `)==-1?[qt]:qt.split(/\n/gm);for(let Pt=0;Pt<mi.length;Pt++){let af=mi[Pt];Te?(f.save(),f.translate(_,y+Pt*Jt),f.rotate(Te),f.fillText(af,0,0),f.restore()):f.fillText(af,_,y+Pt*Jt)}}}X.show&&Vs(on,X.filter(r,$e,u,R,fe),g,p,et,Qe,Ue(X.width*we,3),X.stroke(r,u),X.dash,X.cap);let Zn=d.grid;Zn.show&&Vs(on,Zn.filter(r,$e,u,R,fe),g,g==0?2:1,g==0?en:Mt,g==0?hn:Ot,Ue(Zn.width*we,3),Zn.stroke(r,u),Zn.dash,Zn.cap),H.show&&Vs([et],[1],g==0?1:0,g==0?1:2,g==1?en:Mt,g==1?hn:Ot,Ue(H.width*we,3),H.stroke(r,u),H.dash,H.cap)}pt("drawAxes")}function qi(u){k.forEach((d,p)=>{p>0&&(d._paths=null,u&&(n==1?(d.min=null,d.max=null):d.facets.forEach(g=>{g.min=null,g.max=null})))})}let qo=!1,Ws=!1,xr=[];function Zy(){Ws=!1;for(let u=0;u<xr.length;u++)pt(...xr[u]);xr.length=0}function Vi(){qo||(_6(Md),qo=!0)}function Py(u,d=!1){qo=!0,Ws=d,u(r),Md(),d&&xr.length>0&&queueMicrotask(Zy)}r.batch=Py;function Md(){if(Ns&&(yy(),Ns=!1),Ai&&(py(),Ai=!1),Ao){if(Ce($,er,je),Ce($,Nr,at),Ce($,Dr,le),Ce($,Rr,L),Ce(S,er,je),Ce(S,Nr,at),Ce(S,Dr,le),Ce(S,Rr,L),Ce(h,Dr,jn),Ce(h,Rr,oi),m.width=st(jn*we),m.height=st(oi*we),T.forEach(({_el:u,_show:d,_size:p,_pos:g,side:_})=>{if(u!=null)if(d){let y=_===3||_===0?p:0,x=_%2==1;Ce(u,x?"left":"top",g-y),Ce(u,x?"width":"height",p),Ce(u,x?"top":"left",x?at:je),Ce(u,x?"height":"width",x?L:le),Zl(u,bi)}else Vt(u,bi)}),Mo=Ji=Ds=As=Cs=Ls=Ms=Fs=Rs=null,jt=1,Zr(!0),je!=Ri||at!=ai||le!=Sn||L!=si){qi(!1);let u=le/Sn,d=L/si;if(xt&&!li&&W.left>=0){W.left*=u,W.top*=d,Wi&&Pn(Wi,st(W.left),0,le,L),Hi&&Pn(Hi,0,st(W.top),le,L);for(let p=0;p<nn.length;p++){let g=nn[p];g!=null&&(Mi[p]*=u,Fi[p]*=d,Pn(g,sn(Mi[p]),sn(Fi[p]),le,L))}}if(Re.show&&!Co&&Re.left>=0&&Re.width>0){Re.left*=u,Re.width*=u,Re.top*=d,Re.height*=d;for(let p in Xs)Ce(Ki,p,Re[p])}Ri=je,ai=at,Sn=le,si=L}pt("setSize"),Ao=!1}jn>0&&oi>0&&(f.clearRect(0,0,m.width,m.height),pt("drawClear"),te.forEach(u=>u()),pt("draw")),Re.show&&Co&&(Vo(Re),Co=!1),xt&&li&&(fi(null,!0,!1),li=!1),de.show&&de.live&&tn&&(Ks(),tn=!1),l||(l=!0,r.status=1,pt("ready")),Ir=!1,qo=!1}r.redraw=(u,d)=>{Ai=d||!1,u!==!1?Wn(C,O.min,O.max):Vi()};function Hs(u,d){let p=z[u];if(p.from==null){if(gt==0){let g=p.range(r,d.min,d.max,u);d.min=g[0],d.max=g[1]}if(d.min>d.max){let g=d.min;d.min=d.max,d.max=g}if(gt>1&&d.min!=null&&d.max!=null&&d.max-d.min<1e-16)return;u==C&&p.distr==2&&gt>0&&(d.min=$n(d.min,t[0]),d.max=$n(d.max,t[0]),d.min==d.max&&d.max++),K[u]=d,Ns=!0,Vi()}}r.setScale=Hs;let Bs,Gs,Wi,Hi,Fd,Jd,Bi,Gi,qd,Vd,De,Ve,Vn=!1;const vt=W.drag;let ft=vt.x,mt=vt.y;xt&&(W.x&&(Bs=an(FT,S)),W.y&&(Gs=an(JT,S)),O.ori==0?(Wi=Bs,Hi=Gs):(Wi=Gs,Hi=Bs),De=W.left,Ve=W.top);const Re=r.select=rt({show:!0,over:!0,left:0,width:0,top:0,height:0},e.select),Ki=Re.show?an(MT,Re.over?S:$):null;function Vo(u,d){if(Re.show){for(let p in u)Re[p]=u[p],p in Xs&&Ce(Ki,p,u[p]);d!==!1&&pt("setSelect")}}r.setSelect=Vo;function Ey(u){if(k[u].show)ot&&Zl(Be[u],bi);else if(ot&&Vt(Be[u],bi),xt){let p=Li?nn[0]:nn[u];p!=null&&Pn(p,-10,-10,le,L)}}function Wn(u,d,p){Hs(u,{min:d,max:p})}function xn(u,d,p,g){d.focus!=null&&Dy(u),d.show!=null&&k.forEach((_,y)=>{y>0&&(u==y||u==null)&&(_.show=d.show,Ey(y),n==2?(Wn(_.facets[0].scale,null,null),Wn(_.facets[1].scale,null,null)):Wn(_.scale,null,null),Vi())}),p!==!1&&pt("setSeries",u,d),g&&Pr("setSeries",r,u,d)}r.setSeries=xn;function Uy(u,d){rt(ee[u],d)}function Ny(u,d){u.fill=he(u.fill||null),u.dir=ye(u.dir,-1),d=d??ee.length,ee.splice(d,0,u)}function Oy(u){u==null?ee.length=0:ee.splice(u,1)}r.addBand=Ny,r.setBand=Uy,r.delBand=Oy;function jy(u,d){k[u].alpha=d,xt&&nn[u]!=null&&(nn[u].style.opacity=d),ot&&Be[u]&&(Be[u].style.opacity=d)}let Dn,Hn,di;const Yi={focus:!0};function Dy(u){if(u!=di){let d=u==null,p=In.alpha!=1;k.forEach((g,_)=>{if(n==1||_>0){let y=d||_==0||_==u;g._focus=d?null:y,p&&jy(_,y?1:In.alpha)}}),di=u,p&&Vi()}}ot&&Ci&&zt(Yf,Se,u=>{W._lock||(ui(u),di!=null&&xn(null,Yi,!0,it.setSeries))});function zn(u,d,p){let g=z[d];p&&(u=u/we-(g.ori==1?at:je));let _=le;g.ori==1&&(_=L,u=_-u),g.dir==-1&&(u=_-u);let y=g._min,x=g._max,Z=u/_,N=y+(x-y)*Z,R=g.distr;return R==3?fr(10,N):R==4?a6(N,g.asinh):R==100?g.bwd(N):N}function Ry(u,d){let p=zn(u,C,d);return $n(p,t[0],ct,dt)}r.valToIdx=u=>$n(u,t[0]),r.posToIdx=Ry,r.posToVal=zn,r.valToPos=(u,d,p)=>z[d].ori==0?o(u,z[d],p?Ot:le,p?Mt:0):a(u,z[d],p?hn:L,p?en:0),r.setCursor=(u,d,p)=>{De=u.left,Ve=u.top,fi(null,d,p)};function Wd(u,d){Ce(Ki,er,Re.left=u),Ce(Ki,Dr,Re.width=d)}function Hd(u,d){Ce(Ki,Nr,Re.top=u),Ce(Ki,Rr,Re.height=d)}let zr=O.ori==0?Wd:Hd,Tr=O.ori==1?Wd:Hd;function Ay(){if(ot&&de.live)for(let u=n==2?1:0;u<k.length;u++){if(u==0&&Qt)continue;let d=de.values[u],p=0;for(let g in d)wn[u][p++].firstChild.nodeValue=d[g]}}function Ks(u,d){if(u!=null&&(u.idxs?u.idxs.forEach((p,g)=>{ze[g]=p}):c6(u.idx)||ze.fill(u.idx),de.idx=ze[0]),ot&&de.live){for(let p=0;p<k.length;p++)(p>0||n==1&&!Qt)&&Cy(p,ze[p]);Ay()}tn=!1,d!==!1&&pt("setLegend")}r.setLegend=Ks;function Cy(u,d){let p=k[u],g=u==0&&ue==2?gn:t[u],_;Qt?_=p.values(r,u,d)??Jn:(_=p.value(r,d==null?null:g[d],u,d),_=_==null?Jn:{_}),de.values[u]=_}function fi(u,d,p){qd=De,Vd=Ve,[De,Ve]=W.move(r,De,Ve),W.left=De,W.top=Ve,xt&&(Wi&&Pn(Wi,st(De),0,le,L),Hi&&Pn(Hi,0,st(Ve),le,L));let g,_=ct>dt;Dn=Ee,Hn=null;let y=O.ori==0?le:L,x=O.ori==1?le:L;if(De<0||gt==0||_){g=W.idx=null;for(let Z=0;Z<k.length;Z++){let N=nn[Z];N!=null&&Pn(N,-10,-10,le,L)}Ci&&xn(null,Yi,!0,u==null&&it.setSeries),de.live&&(ze.fill(g),tn=!0)}else{let Z,N,R;n==1&&(Z=O.ori==0?De:Ve,N=zn(Z,C),g=W.idx=$n(N,t[0],ct,dt),R=V(t[0][g],O,y,0));let A=-10,F=-10,ie=0,ke=0,$e=!0,fe="",X="";for(let H=n==2?1:0;H<k.length;H++){let Ae=k[H],Qe=ze[H],ht=Qe==null?null:n==1?t[H][Qe]:t[H][1][Qe],Te=W.dataIdx(r,H,g,N),et=Te==null?null:n==1?t[H][Te]:t[H][1][Te];if(tn=tn||et!=ht||Te!=Qe,ze[H]=Te,H>0&&Ae.show){let Ft=Te==null?-10:Te==g?R:V(n==1?t[0][Te]:t[H][0][Te],O,y,0),Tt=et==null?-10:ne(et,n==1?z[Ae.scale]:z[Ae.facets[1].scale],x,0);if(Ci&&et!=null){let rn=O.ori==1?De:Ve,vn=lt(In.dist(r,H,Te,Tt,rn));if(vn<Dn){let Tn=In.bias;if(Tn!=0){let Jt=zn(rn,Ae.scale),on=et>=0?1:-1,_n=Jt>=0?1:-1;_n==on&&(_n==1?Tn==1?et>=Jt:et<=Jt:Tn==1?et<=Jt:et>=Jt)&&(Dn=vn,Hn=H)}else Dn=vn,Hn=H}}if(tn||Li){let rn,vn;O.ori==0?(rn=Ft,vn=Tt):(rn=Tt,vn=Ft);let Tn,Jt,on,_n,Zn,Zt,qt=!0,mi=kt.bbox;if(mi!=null){qt=!1;let Pt=mi(r,H);on=Pt.left,_n=Pt.top,Tn=Pt.width,Jt=Pt.height}else on=rn,_n=vn,Tn=Jt=kt.size(r,H);if(Zt=kt.fill(r,H),Zn=kt.stroke(r,H),Li)H==Hn&&Dn<=In.prox&&(A=on,F=_n,ie=Tn,ke=Jt,$e=qt,fe=Zt,X=Zn);else{let Pt=nn[H];Pt!=null&&(Mi[H]=on,Fi[H]=_n,rm(Pt,Tn,Jt,qt),nm(Pt,Zt,Zn),Pn(Pt,sn(on),sn(_n),le,L))}}}}if(Li){let H=In.prox,Ae=di==null?Dn<=H:Dn>H||Hn!=di;if(tn||Ae){let Qe=nn[0];Qe!=null&&(Mi[0]=A,Fi[0]=F,rm(Qe,ie,ke,$e),nm(Qe,fe,X),Pn(Qe,sn(A),sn(F),le,L))}}}if(Re.show&&Vn)if(u!=null){let[Z,N]=it.scales,[R,A]=it.match,[F,ie]=u.cursor.sync.scales,ke=u.cursor.drag;if(ft=ke._x,mt=ke._y,ft||mt){let{left:$e,top:fe,width:X,height:H}=u.select,Ae=u.scales[F].ori,Qe=u.posToVal,ht,Te,et,Ft,Tt,rn=Z!=null&&R(Z,F),vn=N!=null&&A(N,ie);rn&&ft?(Ae==0?(ht=$e,Te=X):(ht=fe,Te=H),et=z[Z],Ft=V(Qe(ht,F),et,y,0),Tt=V(Qe(ht+Te,F),et,y,0),zr(yn(Ft,Tt),lt(Tt-Ft))):zr(0,y),vn&&mt?(Ae==1?(ht=$e,Te=X):(ht=fe,Te=H),et=z[N],Ft=ne(Qe(ht,ie),et,x,0),Tt=ne(Qe(ht+Te,ie),et,x,0),Tr(yn(Ft,Tt),lt(Tt-Ft))):Tr(0,x)}else Qs()}else{let Z=lt(qd-Fd),N=lt(Vd-Jd);if(O.ori==1){let ie=Z;Z=N,N=ie}ft=vt.x&&Z>=vt.dist,mt=vt.y&&N>=vt.dist;let R=vt.uni;R!=null?ft&&mt&&(ft=Z>=R,mt=N>=R,!ft&&!mt&&(N>Z?mt=!0:ft=!0)):vt.x&&vt.y&&(ft||mt)&&(ft=mt=!0);let A,F;ft&&(O.ori==0?(A=Bi,F=De):(A=Gi,F=Ve),zr(yn(A,F),lt(F-A)),mt||Tr(0,x)),mt&&(O.ori==1?(A=Bi,F=De):(A=Gi,F=Ve),Tr(yn(A,F),lt(F-A)),ft||zr(0,y)),!ft&&!mt&&(zr(0,0),Tr(0,0))}if(vt._x=ft,vt._y=mt,u==null){if(p){if(of!=null){let[Z,N]=it.scales;it.values[0]=Z!=null?zn(O.ori==0?De:Ve,Z):null,it.values[1]=N!=null?zn(O.ori==1?De:Ve,N):null}Pr(ol,r,De,Ve,le,L,g)}if(Ci){let Z=p&&it.setSeries,N=In.prox;di==null?Dn<=N&&xn(Hn,Yi,!0,Z):Dn>N?xn(null,Yi,!0,Z):Hn!=di&&xn(Hn,Yi,!0,Z)}}tn&&(de.idx=g,Ks()),d!==!1&&pt("setCursor")}let Bn=null;Object.defineProperty(r,"rect",{get(){return Bn==null&&Zr(!1),Bn}});function Zr(u=!1){u?Bn=null:(Bn=S.getBoundingClientRect(),pt("syncRect",Bn))}function Bd(u,d,p,g,_,y,x){W._lock||Vn&&u!=null&&u.movementX==0&&u.movementY==0||(Ys(u,d,p,g,_,y,x,!1,u!=null),u!=null?fi(null,!0,!0):fi(d,!0,!1))}function Ys(u,d,p,g,_,y,x,Z,N){if(Bn==null&&Zr(!1),ui(u),u!=null)p=u.clientX-Bn.left,g=u.clientY-Bn.top;else{if(p<0||g<0){De=-10,Ve=-10;return}let[R,A]=it.scales,F=d.cursor.sync,[ie,ke]=F.values,[$e,fe]=F.scales,[X,H]=it.match,Ae=d.axes[0].side%2==1,Qe=O.ori==0?le:L,ht=O.ori==1?le:L,Te=Ae?y:_,et=Ae?_:y,Ft=Ae?g:p,Tt=Ae?p:g;if($e!=null?p=X(R,$e)?s(ie,z[R],Qe,0):-10:p=Qe*(Ft/Te),fe!=null?g=H(A,fe)?s(ke,z[A],ht,0):-10:g=ht*(Tt/et),O.ori==1){let rn=p;p=g,g=rn}}N&&(d==null||d.cursor.event.type==ol)&&((p<=1||p>=le-1)&&(p=vi(p,le)),(g<=1||g>=L-1)&&(g=vi(g,L))),Z?(Fd=p,Jd=g,[Bi,Gi]=W.move(r,p,g)):(De=p,Ve=g)}const Xs={width:0,height:0,left:0,top:0};function Qs(){Vo(Xs,!1)}let Gd,Kd,Yd,Xd;function Qd(u,d,p,g,_,y,x){Vn=!0,ft=mt=vt._x=vt._y=!1,Ys(u,d,p,g,_,y,x,!0,!1),u!=null&&(zt(al,zl,ef,!1),Pr(Gf,r,Bi,Gi,le,L,null));let{left:Z,top:N,width:R,height:A}=Re;Gd=Z,Kd=N,Yd=R,Xd=A}function ef(u,d,p,g,_,y,x){Vn=vt._x=vt._y=!1,Ys(u,d,p,g,_,y,x,!1,!0);let{left:Z,top:N,width:R,height:A}=Re,F=R>0||A>0,ie=Gd!=Z||Kd!=N||Yd!=R||Xd!=A;if(F&&ie&&Vo(Re),vt.setScale&&F&&ie){let ke=Z,$e=R,fe=N,X=A;if(O.ori==1&&(ke=N,$e=A,fe=Z,X=R),ft&&Wn(C,zn(ke,C),zn(ke+$e,C)),mt)for(let H in z){let Ae=z[H];H!=C&&Ae.from==null&&Ae.min!=Ee&&Wn(H,zn(fe+X,H),zn(fe,H))}Qs()}else W.lock&&(W._lock=!W._lock,fi(d,!0,u!=null));u!=null&&(Di(al,zl),Pr(al,r,De,Ve,le,L,null))}function Ly(u,d,p,g,_,y,x){if(W._lock)return;ui(u);let Z=Vn;if(Vn){let N=!0,R=!0,A=10,F,ie;O.ori==0?(F=ft,ie=mt):(F=mt,ie=ft),F&&ie&&(N=De<=A||De>=le-A,R=Ve<=A||Ve>=L-A),F&&N&&(De=De<Bi?0:le),ie&&R&&(Ve=Ve<Gi?0:L),fi(null,!0,!0),Vn=!1}De=-10,Ve=-10,ze.fill(null),fi(null,!0,!0),Z&&(Vn=Z)}function tf(u,d,p,g,_,y,x){W._lock||(ui(u),js(),Qs(),u!=null&&Pr(Xf,r,De,Ve,le,L,null))}function nf(){T.forEach(xZ),Os(r.width,r.height,!0)}wi(wa,ir,nf);const Xi={};Xi.mousedown=Qd,Xi.mousemove=Bd,Xi.mouseup=ef,Xi.dblclick=tf,Xi.setSeries=(u,d,p,g)=>{let _=it.match[2];p=_(r,d,p),p!=-1&&xn(p,g,!0,!1)},xt&&(zt(Gf,S,Qd),zt(ol,S,Bd),zt(Kf,S,u=>{ui(u),Zr(!1)}),zt(Yf,S,Ly),zt(Xf,S,tf),jl.add(r),r.syncRect=Zr);const Wo=r.hooks=e.hooks||{};function pt(u,d,p){Ws?xr.push([u,d,p]):u in Wo&&Wo[u].forEach(g=>{g.call(null,r,d,p)})}(e.plugins||[]).forEach(u=>{for(let d in u.hooks)Wo[d]=(Wo[d]||[]).concat(u.hooks[d])});const rf=(u,d,p)=>p,it=rt({key:null,setSeries:!1,filters:{pub:um,sub:um},scales:[C,k[1]?k[1].scale:null],match:[cm,cm,rf],values:[null,null]},W.sync);it.match.length==2&&it.match.push(rf),W.sync=it;const of=it.key,el=q$(of);function Pr(u,d,p,g,_,y,x){it.filters.pub(u,d,p,g,_,y,x)&&el.pub(u,d,p,g,_,y,x)}el.sub(r);function My(u,d,p,g,_,y,x){it.filters.sub(u,d,p,g,_,y,x)&&Xi[u](null,d,p,g,_,y,x)}r.pub=My;function Fy(){el.unsub(r),jl.delete(r),ri.clear(),Pl(wa,ir,nf),c.remove(),Se?.remove(),pt("destroy")}r.destroy=Fy;function tl(){pt("init",e,t),Od(t||e.data,!1),K[C]?Hs(C,K[C]):js(),Co=Re.show&&(Re.width>0||Re.height>0),li=tn=!0,Os(e.width,e.height)}return k.forEach(Ud),T.forEach($y),i?i instanceof HTMLElement?(i.appendChild(c),tl()):i(r,tl):tl(),r}St.assign=rt;St.fmtNum=bd;St.rangeNum=Sa;St.rangeLog=xs;St.rangeAsinh=vd;St.orient=Ni;St.pxRatio=we;St.join=v6;St.fmtDate=yd,St.tzDate=T6;St.sync=q$;{St.addGap=cZ,St.clipGaps=Zs;let e=St.paths={points:K$};e.linear=X$,e.stepped=mZ,e.bars=pZ,e.spline=gZ}function Rl(e,t=0){return e<=0?[]:Array.from({length:e},(i,r)=>t+r+1)}function zZ(e,t){return e<=0&&t<=0?[]:[...Rl(e,0),...Rl(t,e)]}function ny(e,t){return t==="failed"||t==="cancelled"||e.kernel_complete===!0?!0:t==="completed"}function dl(e){return`${e} (panel)`}function TZ(e){if(String(e.training_progress_phase??e.progress_phase??"").toLowerCase()==="floop"||String(e.job_type??"").toLowerCase().includes("floop"))return!0;const r=e.run_config;return!!(r&&typeof r=="object"&&String(r.model??"").toLowerCase().includes("floop"))}function ZZ(e){const t=e.training_curve_source_batches,i=[],r=t&&typeof t.ordering=="number"?t.ordering:null,n=t&&typeof t.convergence=="number"?t.convergence:null,o=Array.isArray(e.ordering_errors)?e.ordering_errors.length:0,a=Array.isArray(e.convergence_errors)?e.convergence_errors.length:0;r!=null&&r>o&&i.push(`${o} of ${r.toLocaleString()} ordering batch samples`),n!=null&&n>a&&i.push(`${a} of ${n.toLocaleString()} convergence batch samples`);const s=e.te_evaluation,l=s?.te_panel_size,c=s?.te_panel_n_train;if(typeof l=="number"&&typeof c=="number"&&c>0){const $=s?.te_panel_stratified===!0?", stratified by mesh":"";i.push(`TE curve uses a fixed panel of ${l.toLocaleString()} of ${c.toLocaleString()} training rows${$}`)}const m=e.panel_topographic_error,f=e.map_topographic_error;return m!=null&&f!=null&&Number.isFinite(Number(m))&&Number.isFinite(Number(f))&&i.push(`Panel TE ${Number(m).toFixed(4)} · Map TE ${Number(f).toFixed(4)} (full training set)`),i.length===0?null:`${r!=null||n!=null?"Displaying uniformly subsampled QE+TE curves (≤1000 points/phase, joint indices): ":""}${i.join("; ")}.`.replace(/: ;/,":")}function fl(e,t){if(t<=0)return[];if(e.length===t)return e;if(e.length===0)return Array(t).fill(null);if(e.length>=t)return e.slice(0,t);const i=t-e.length;return[...Array(i).fill(null),...e]}function iy(e){return e==="completed"||e==="failed"||e==="cancelled"}function Td(e){const t=String(e.status??"");return t!=="completed"?iy(t):(e.finalize_job_id!=null&&String(e.finalize_job_id)!==""?String(e.finalize_job_id):null)?String(e.finalize_status??"")==="completed":!0}function Al(e){const t=String(e.status??"");if(!iy(t))return!0;if(t!=="completed"||e.finalize_failed===!0)return!1;const i=e.finalize_job_id!=null&&String(e.finalize_job_id)!==""?String(e.finalize_job_id):null,r=e.finalize_status!=null&&String(e.finalize_status)!==""?String(e.finalize_status):null;return i!=null&&r!=="completed"}const En=new ET({name:"Training Monitor",version:"1.0.0"},{},{autoResize:!0});let Um=!1,Nm=!1;const PZ=document.getElementById("progress-fill"),EZ=document.getElementById("progress-pct"),Cl=document.getElementById("elapsed"),UZ=document.getElementById("phase"),oa=document.getElementById("eta"),Ll=document.getElementById("job-id"),Dt=document.getElementById("status-badge"),NZ=document.getElementById("params"),Wt=document.getElementById("advisory-banner");document.getElementById("curve");const Do=document.getElementById("chart-target"),Si=document.getElementById("curve-fallback"),ry=document.getElementById("chart-legend"),OZ=document.getElementById("metric-scalars"),to=document.getElementById("cancel-btn"),hi=document.getElementById("open-results-btn"),nr=document.getElementById("copy-monitor-url"),Om=document.getElementById("open-standalone"),ml=document.getElementById("hitgrid-container"),aa=document.getElementById("hitgrid-canvas"),jZ=document.getElementById("hitgrid-meta"),Ml=5e3,pl=2e3;let tt="",hl=null,Jr=null,qr=null,gl=!1,jm=!1,Kn=null;function Zd(e){return`/viz/results-explorer?mode=standalone&job_id=${encodeURIComponent(e)}`}function oy(e){const t=String(e.id??e.job_id??tt);if(!t||!hi)return;hi.style.display="inline-flex";const i=String(e.status??"")==="completed"&&Td(e);hi.href=Zd(t),i?(hi.classList.remove("nav-btn-disabled"),hi.title="Browse figures for this job"):(hi.classList.add("nav-btn-disabled"),hi.title="Available when training and finalize (if any) complete")}async function DZ(e){if(jm||String(e.status??"")!=="completed"||!Td(e))return;jm=!0,oy(e);const t=String(e.id??e.job_id??tt);if(!t)return;const i=Zd(t);if(kr){window.open(i,"_blank","noopener,noreferrer");return}try{await En.callServerTool({name:"results_explorer",arguments:{job_id:t}})}catch{}}function Cr(e){if(e<60)return`${e}s`;const t=Math.floor(e/60),i=e%60;return t<60?`${t}m ${i}s`:`${Math.floor(t/60)}h ${t%60}m`}function RZ(e){if(Jr)return;hl=e;const t=()=>{if(hl==null)return;const i=Math.max(0,Math.round((Date.now()-hl)/1e3));Cl.textContent=Cr(i)};t(),Jr=setInterval(t,1e3)}function Vr(){Jr&&(clearInterval(Jr),Jr=null)}function AZ(e,t,i){const r=ny(e,t);r&&Vr();const n=e.training_elapsed_sec!=null?Number(e.training_elapsed_sec):e.kernel_elapsed_sec!=null?Number(e.kernel_elapsed_sec):null,o=e.wall_elapsed_sec!=null?Number(e.wall_elapsed_sec):null;if(n!=null&&n>=0)Cl.textContent=r?`Training ${Cr(Math.round(n))}`:Cr(Math.round(n));else if(!r&&e.started_at&&typeof e.started_at=="string"){const s=Date.parse(e.started_at);Number.isNaN(s)||RZ(s)}else e.training_duration_seconds&&(Cl.textContent=`${e.training_duration_seconds}s`);if(r){if(i&&o!=null&&o>=0){const s=n!=null?Math.round(n):null;oa.textContent=s==null||Math.abs(Math.round(o)-s)>=2?`Wall ${Cr(Math.round(o))}`:""}else oa.textContent="";return}const a=e.training_eta_sec!=null?Number(e.training_eta_sec):null;a!=null&&a>0?oa.textContent=`ETA ~${Cr(Math.round(a))}`:oa.textContent=""}function ay(){const e=Do.clientWidth;if(e>0)return Math.max(e,200);const t=En.getHostContext?.()?.containerDimensions,i=t?.width??t?.maxWidth;return Math.max(typeof i=="number"?i:360,200)}function Nn(e){return Array.isArray(e)?e.map(t=>Number(t)).filter(t=>Number.isFinite(t)):[]}function Fl(e){return Nn(e.ordering_errors).length>0||Nn(e.convergence_errors).length>0||Nn(e.ordering_topographic_errors).length>0||Nn(e.convergence_topographic_errors).length>0}function sa(e){return Array(e).fill(null)}function sy(){Kn&&(Kn.destroy(),Kn=null),Do.innerHTML=""}function CZ(e,t){ry.innerHTML=e.map((i,r)=>`<span class="legend-item"><span class="legend-swatch" style="background:${t[r]}"></span>${i}</span>`).join("")}function Dm(e,t,i="Batch sample"){if(sy(),e.length<2)return;Si.style.display="none";const r=getComputedStyle(document.documentElement).getPropertyValue("--accent").trim()||"#4f46e5",n=getComputedStyle(document.documentElement).getPropertyValue("--coral").trim()||"#f97316",o=getComputedStyle(document.documentElement).getPropertyValue("--text2").trim()||"#666",a=getComputedStyle(document.documentElement).getPropertyValue("--border").trim()||"#e0e0e0",s=t.map(f=>({...f,stroke:f.stroke==="accent"?r:f.stroke==="coral"?n:f.stroke})),l={width:ay(),height:160,cursor:{show:!0},select:{show:!1,left:0,top:0,width:0,height:0},scales:{x:{time:!1},y:{auto:!0},y2:{auto:!0}},axes:[{stroke:o,grid:{stroke:a,width:1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:i,labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:32},{scale:"y",stroke:o,grid:{stroke:a,width:1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:"QE",labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:48},{scale:"y2",side:1,stroke:o,grid:{show:!1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:"TE",labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:48}],series:[{},...s.map(f=>({label:f.label,scale:f.scale,stroke:f.stroke,width:f.scale==="y"?2:1.5,dash:f.dash,spanGaps:!1}))]},c=[e,...s.map(f=>f.values)];Kn=new St(l,c,Do),CZ(s.map(f=>f.label),s.map(f=>f.stroke));const m=ZZ(ly);m&&ry.insertAdjacentHTML("beforeend",`<div class="curve-source-note" style="margin-top:6px;font-size:11px;color:var(--text2);">${m}</div>`)}let ly={};function LZ(e,t){ny(e,t);const i=e;ly=i;const r=Nn(i.ordering_errors),n=Nn(i.convergence_errors),o=Nn(i.ordering_topographic_errors),a=Nn(i.convergence_topographic_errors);if(r.length===0&&n.length===0&&o.length===0&&a.length===0)return!1;const s=TZ(i),l=r.length,c=n.length,m=s?Math.max(l,o.length,1):Math.max(l+c,1),f=s?Rl(m):zZ(l,c);if(f.length<2)return!1;if(s){const J=r,k=fl(o,m),T=[{label:"QE (FLooP steps)",values:J,scale:"y",stroke:"accent"}];return o.length>0&&T.push({label:dl("TE (FLooP steps)"),values:k,scale:"y2",stroke:"accent",dash:[4,4]}),Dm(f,T,"FLooP step"),!0}const h=[...r,...sa(c)],$=[...sa(l),...n],S=[...fl(o,l),...sa(c)],U=[...sa(l),...fl(a,c)],D=[];return l>0&&D.push({label:"QE ordering",values:h,scale:"y",stroke:"accent"}),c>0&&D.push({label:"QE convergence",values:$,scale:"y",stroke:"coral"}),o.length>0&&D.push({label:dl("TE ordering"),values:S,scale:"y2",stroke:"accent",dash:[4,4]}),a.length>0&&D.push({label:dl("TE convergence"),values:U,scale:"y2",stroke:"coral",dash:[4,4]}),Dm(f,D,"Batch sample"),!0}function MZ(e){const t=Nn(e.hit_grid),i=Nn(e.hit_grid_dims);if(t.length===0||i.length<2){ml.style.display="none";return}const r=Math.max(1,Math.round(i[0])),n=Math.max(1,Math.round(i[1]));if(r*n!==t.length){ml.style.display="none";return}ml.style.display="block",aa.width=r,aa.height=n,aa.style.height=`${Math.min(220,n*8)}px`;const o=aa.getContext("2d");if(!o)return;const a=t.reduce((U,D)=>D>U?D:U,0)||1,s=getComputedStyle(document.documentElement).getPropertyValue("--accent").trim()||"#4f46e5",l=/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(s.replace("#","")),c=l?parseInt(l[1],16):79,m=l?parseInt(l[2],16):70,f=l?parseInt(l[3],16):229,h=o.createImageData(r,n);let $=0;for(let U=0;U<t.length;U++){const D=t[U]/a;t[U]>0&&$++;const J=U%r,T=(Math.floor(U/r)*r+J)*4;h.data[T]=Math.round(245-D*(245-c)),h.data[T+1]=Math.round(245-D*(245-m)),h.data[T+2]=Math.round(245-D*(245-f)),h.data[T+3]=255}o.putImageData(h,0,0);const S=t.reduce((U,D)=>U+D,0);jZ.textContent=`${r}×${n} nodes · ${$} occupied (${($/t.length*100).toFixed(0)}%) · ${S} sampled hits · max ${a}`}function FZ(e){const t=e.quantization_error??e.qe,i=e.panel_topographic_error??e.epoch_topographic_error??e.topographic_error,r=e.map_topographic_error,n=[];t!=null&&Number.isFinite(Number(t))&&n.push(`QE ${Number(t).toFixed(4)}`),i!=null&&Number.isFinite(Number(i))&&n.push(`Panel TE ${Number(i).toFixed(4)}`),r!=null&&Number.isFinite(Number(r))&&n.push(`Map TE ${Number(r).toFixed(4)}`),OZ.textContent=n.length>0?n.join(" · "):""}function uy(e){return e==="completed"||e==="failed"||e==="cancelled"}function cy(e){const t=e.standaloneUrl;if(t)return t;if(kr&&tt)return`${window.location.origin}/viz/training-monitor?mode=standalone&job_id=${encodeURIComponent(tt)}`}function JZ(e){const t=e.relatedUrls;if(t?.resultsExplorer)return t.resultsExplorer;if(kr&&tt)return`${window.location.origin}/viz/results-explorer?mode=standalone&job_id=${encodeURIComponent(tt)}`}function qZ(e){const t=cy(e);t&&(nr.style.display="inline-block",nr.dataset.url=t,Om.href=t,Om.style.display="inline-block")}function VZ(e,t,i){if(i||t!=="completed")return;const r=JZ(e);r&&(Wt.style.display="block",Wt.innerHTML=`Training complete. <a href="${r}" target="_blank" rel="noreferrer">Open results explorer</a> to browse figures.`)}nr.addEventListener("click",async()=>{const e=nr.dataset.url||cy({standaloneUrl:void 0});if(e)try{await navigator.clipboard.writeText(e),nr.textContent="Copied!",setTimeout(()=>{nr.textContent="Copy monitor URL"},1500)}catch{window.prompt("Copy monitor URL:",e)}});function WZ(e,t){if(kr||Um||Nm||!Td(e))return;const i=String(e.id??e.job_id??tt);if(!i)return;Um=!0;const r=[`Training job ${i} ${t}.`],n=e.quantization_error??e.qe,o=e.map_topographic_error??e.topographic_error,a=e.panel_topographic_error??e.epoch_topographic_error;if(n!=null&&Number.isFinite(Number(n))&&r.push(`Final QE: ${Number(n).toFixed(4)}`),a!=null&&Number.isFinite(Number(a))&&r.push(`Final panel TE: ${Number(a).toFixed(4)}`),o!=null&&Number.isFinite(Number(o))&&r.push(`Final map TE: ${Number(o).toFixed(4)}`),e.finalize_failed===!0)r.push(`Finalize failed${e.finalize_failure_stage?` [${String(e.finalize_failure_stage)}]`:""}: ${String(e.finalize_error??"results not uploaded")}. Results may be unavailable until finalize is retried.`);else if(t==="completed"){r.push(`Use results(get, job_id="${i}") for metrics, then results_explorer(job_id="${i}") to browse figures.`);const s=Zd(i);r.push(`Results explorer: ${s}`)}DZ(e),En.updateModelContext({content:[{type:"text",text:r.join(`
142
+ `)==-1?[qt]:qt.split(/\n/gm);for(let Pt=0;Pt<mi.length;Pt++){let af=mi[Pt];Te?(f.save(),f.translate(_,y+Pt*Jt),f.rotate(Te),f.fillText(af,0,0),f.restore()):f.fillText(af,_,y+Pt*Jt)}}}X.show&&Vs(on,X.filter(r,$e,u,R,fe),g,p,et,Qe,Ue(X.width*we,3),X.stroke(r,u),X.dash,X.cap);let Zn=d.grid;Zn.show&&Vs(on,Zn.filter(r,$e,u,R,fe),g,g==0?2:1,g==0?en:Mt,g==0?hn:Ot,Ue(Zn.width*we,3),Zn.stroke(r,u),Zn.dash,Zn.cap),H.show&&Vs([et],[1],g==0?1:0,g==0?1:2,g==1?en:Mt,g==1?hn:Ot,Ue(H.width*we,3),H.stroke(r,u),H.dash,H.cap)}pt("drawAxes")}function qi(u){k.forEach((d,p)=>{p>0&&(d._paths=null,u&&(n==1?(d.min=null,d.max=null):d.facets.forEach(g=>{g.min=null,g.max=null})))})}let qo=!1,Ws=!1,xr=[];function Zy(){Ws=!1;for(let u=0;u<xr.length;u++)pt(...xr[u]);xr.length=0}function Vi(){qo||(_6(Md),qo=!0)}function Py(u,d=!1){qo=!0,Ws=d,u(r),Md(),d&&xr.length>0&&queueMicrotask(Zy)}r.batch=Py;function Md(){if(Ns&&(yy(),Ns=!1),Ai&&(py(),Ai=!1),Ao){if(Ce($,er,je),Ce($,Nr,at),Ce($,Dr,le),Ce($,Rr,L),Ce(S,er,je),Ce(S,Nr,at),Ce(S,Dr,le),Ce(S,Rr,L),Ce(h,Dr,jn),Ce(h,Rr,oi),m.width=st(jn*we),m.height=st(oi*we),T.forEach(({_el:u,_show:d,_size:p,_pos:g,side:_})=>{if(u!=null)if(d){let y=_===3||_===0?p:0,x=_%2==1;Ce(u,x?"left":"top",g-y),Ce(u,x?"width":"height",p),Ce(u,x?"top":"left",x?at:je),Ce(u,x?"height":"width",x?L:le),Zl(u,bi)}else Vt(u,bi)}),Mo=Ji=Ds=As=Cs=Ls=Ms=Fs=Rs=null,jt=1,Zr(!0),je!=Ri||at!=ai||le!=Sn||L!=si){qi(!1);let u=le/Sn,d=L/si;if(xt&&!li&&W.left>=0){W.left*=u,W.top*=d,Wi&&Pn(Wi,st(W.left),0,le,L),Hi&&Pn(Hi,0,st(W.top),le,L);for(let p=0;p<nn.length;p++){let g=nn[p];g!=null&&(Mi[p]*=u,Fi[p]*=d,Pn(g,sn(Mi[p]),sn(Fi[p]),le,L))}}if(Re.show&&!Co&&Re.left>=0&&Re.width>0){Re.left*=u,Re.width*=u,Re.top*=d,Re.height*=d;for(let p in Xs)Ce(Ki,p,Re[p])}Ri=je,ai=at,Sn=le,si=L}pt("setSize"),Ao=!1}jn>0&&oi>0&&(f.clearRect(0,0,m.width,m.height),pt("drawClear"),te.forEach(u=>u()),pt("draw")),Re.show&&Co&&(Vo(Re),Co=!1),xt&&li&&(fi(null,!0,!1),li=!1),de.show&&de.live&&tn&&(Ks(),tn=!1),l||(l=!0,r.status=1,pt("ready")),Ir=!1,qo=!1}r.redraw=(u,d)=>{Ai=d||!1,u!==!1?Wn(C,O.min,O.max):Vi()};function Hs(u,d){let p=z[u];if(p.from==null){if(gt==0){let g=p.range(r,d.min,d.max,u);d.min=g[0],d.max=g[1]}if(d.min>d.max){let g=d.min;d.min=d.max,d.max=g}if(gt>1&&d.min!=null&&d.max!=null&&d.max-d.min<1e-16)return;u==C&&p.distr==2&&gt>0&&(d.min=$n(d.min,t[0]),d.max=$n(d.max,t[0]),d.min==d.max&&d.max++),K[u]=d,Ns=!0,Vi()}}r.setScale=Hs;let Bs,Gs,Wi,Hi,Fd,Jd,Bi,Gi,qd,Vd,De,Ve,Vn=!1;const vt=W.drag;let ft=vt.x,mt=vt.y;xt&&(W.x&&(Bs=an(FT,S)),W.y&&(Gs=an(JT,S)),O.ori==0?(Wi=Bs,Hi=Gs):(Wi=Gs,Hi=Bs),De=W.left,Ve=W.top);const Re=r.select=rt({show:!0,over:!0,left:0,width:0,top:0,height:0},e.select),Ki=Re.show?an(MT,Re.over?S:$):null;function Vo(u,d){if(Re.show){for(let p in u)Re[p]=u[p],p in Xs&&Ce(Ki,p,u[p]);d!==!1&&pt("setSelect")}}r.setSelect=Vo;function Ey(u){if(k[u].show)ot&&Zl(Be[u],bi);else if(ot&&Vt(Be[u],bi),xt){let p=Li?nn[0]:nn[u];p!=null&&Pn(p,-10,-10,le,L)}}function Wn(u,d,p){Hs(u,{min:d,max:p})}function xn(u,d,p,g){d.focus!=null&&Dy(u),d.show!=null&&k.forEach((_,y)=>{y>0&&(u==y||u==null)&&(_.show=d.show,Ey(y),n==2?(Wn(_.facets[0].scale,null,null),Wn(_.facets[1].scale,null,null)):Wn(_.scale,null,null),Vi())}),p!==!1&&pt("setSeries",u,d),g&&Pr("setSeries",r,u,d)}r.setSeries=xn;function Uy(u,d){rt(ee[u],d)}function Ny(u,d){u.fill=he(u.fill||null),u.dir=ye(u.dir,-1),d=d??ee.length,ee.splice(d,0,u)}function Oy(u){u==null?ee.length=0:ee.splice(u,1)}r.addBand=Ny,r.setBand=Uy,r.delBand=Oy;function jy(u,d){k[u].alpha=d,xt&&nn[u]!=null&&(nn[u].style.opacity=d),ot&&Be[u]&&(Be[u].style.opacity=d)}let Dn,Hn,di;const Yi={focus:!0};function Dy(u){if(u!=di){let d=u==null,p=In.alpha!=1;k.forEach((g,_)=>{if(n==1||_>0){let y=d||_==0||_==u;g._focus=d?null:y,p&&jy(_,y?1:In.alpha)}}),di=u,p&&Vi()}}ot&&Ci&&zt(Yf,Se,u=>{W._lock||(ui(u),di!=null&&xn(null,Yi,!0,it.setSeries))});function zn(u,d,p){let g=z[d];p&&(u=u/we-(g.ori==1?at:je));let _=le;g.ori==1&&(_=L,u=_-u),g.dir==-1&&(u=_-u);let y=g._min,x=g._max,Z=u/_,N=y+(x-y)*Z,R=g.distr;return R==3?fr(10,N):R==4?a6(N,g.asinh):R==100?g.bwd(N):N}function Ry(u,d){let p=zn(u,C,d);return $n(p,t[0],ct,dt)}r.valToIdx=u=>$n(u,t[0]),r.posToIdx=Ry,r.posToVal=zn,r.valToPos=(u,d,p)=>z[d].ori==0?o(u,z[d],p?Ot:le,p?Mt:0):a(u,z[d],p?hn:L,p?en:0),r.setCursor=(u,d,p)=>{De=u.left,Ve=u.top,fi(null,d,p)};function Wd(u,d){Ce(Ki,er,Re.left=u),Ce(Ki,Dr,Re.width=d)}function Hd(u,d){Ce(Ki,Nr,Re.top=u),Ce(Ki,Rr,Re.height=d)}let zr=O.ori==0?Wd:Hd,Tr=O.ori==1?Wd:Hd;function Ay(){if(ot&&de.live)for(let u=n==2?1:0;u<k.length;u++){if(u==0&&Qt)continue;let d=de.values[u],p=0;for(let g in d)wn[u][p++].firstChild.nodeValue=d[g]}}function Ks(u,d){if(u!=null&&(u.idxs?u.idxs.forEach((p,g)=>{ze[g]=p}):c6(u.idx)||ze.fill(u.idx),de.idx=ze[0]),ot&&de.live){for(let p=0;p<k.length;p++)(p>0||n==1&&!Qt)&&Cy(p,ze[p]);Ay()}tn=!1,d!==!1&&pt("setLegend")}r.setLegend=Ks;function Cy(u,d){let p=k[u],g=u==0&&ue==2?gn:t[u],_;Qt?_=p.values(r,u,d)??Jn:(_=p.value(r,d==null?null:g[d],u,d),_=_==null?Jn:{_}),de.values[u]=_}function fi(u,d,p){qd=De,Vd=Ve,[De,Ve]=W.move(r,De,Ve),W.left=De,W.top=Ve,xt&&(Wi&&Pn(Wi,st(De),0,le,L),Hi&&Pn(Hi,0,st(Ve),le,L));let g,_=ct>dt;Dn=Ee,Hn=null;let y=O.ori==0?le:L,x=O.ori==1?le:L;if(De<0||gt==0||_){g=W.idx=null;for(let Z=0;Z<k.length;Z++){let N=nn[Z];N!=null&&Pn(N,-10,-10,le,L)}Ci&&xn(null,Yi,!0,u==null&&it.setSeries),de.live&&(ze.fill(g),tn=!0)}else{let Z,N,R;n==1&&(Z=O.ori==0?De:Ve,N=zn(Z,C),g=W.idx=$n(N,t[0],ct,dt),R=V(t[0][g],O,y,0));let A=-10,F=-10,ie=0,ke=0,$e=!0,fe="",X="";for(let H=n==2?1:0;H<k.length;H++){let Ae=k[H],Qe=ze[H],ht=Qe==null?null:n==1?t[H][Qe]:t[H][1][Qe],Te=W.dataIdx(r,H,g,N),et=Te==null?null:n==1?t[H][Te]:t[H][1][Te];if(tn=tn||et!=ht||Te!=Qe,ze[H]=Te,H>0&&Ae.show){let Ft=Te==null?-10:Te==g?R:V(n==1?t[0][Te]:t[H][0][Te],O,y,0),Tt=et==null?-10:ne(et,n==1?z[Ae.scale]:z[Ae.facets[1].scale],x,0);if(Ci&&et!=null){let rn=O.ori==1?De:Ve,vn=lt(In.dist(r,H,Te,Tt,rn));if(vn<Dn){let Tn=In.bias;if(Tn!=0){let Jt=zn(rn,Ae.scale),on=et>=0?1:-1,_n=Jt>=0?1:-1;_n==on&&(_n==1?Tn==1?et>=Jt:et<=Jt:Tn==1?et<=Jt:et>=Jt)&&(Dn=vn,Hn=H)}else Dn=vn,Hn=H}}if(tn||Li){let rn,vn;O.ori==0?(rn=Ft,vn=Tt):(rn=Tt,vn=Ft);let Tn,Jt,on,_n,Zn,Zt,qt=!0,mi=kt.bbox;if(mi!=null){qt=!1;let Pt=mi(r,H);on=Pt.left,_n=Pt.top,Tn=Pt.width,Jt=Pt.height}else on=rn,_n=vn,Tn=Jt=kt.size(r,H);if(Zt=kt.fill(r,H),Zn=kt.stroke(r,H),Li)H==Hn&&Dn<=In.prox&&(A=on,F=_n,ie=Tn,ke=Jt,$e=qt,fe=Zt,X=Zn);else{let Pt=nn[H];Pt!=null&&(Mi[H]=on,Fi[H]=_n,rm(Pt,Tn,Jt,qt),nm(Pt,Zt,Zn),Pn(Pt,sn(on),sn(_n),le,L))}}}}if(Li){let H=In.prox,Ae=di==null?Dn<=H:Dn>H||Hn!=di;if(tn||Ae){let Qe=nn[0];Qe!=null&&(Mi[0]=A,Fi[0]=F,rm(Qe,ie,ke,$e),nm(Qe,fe,X),Pn(Qe,sn(A),sn(F),le,L))}}}if(Re.show&&Vn)if(u!=null){let[Z,N]=it.scales,[R,A]=it.match,[F,ie]=u.cursor.sync.scales,ke=u.cursor.drag;if(ft=ke._x,mt=ke._y,ft||mt){let{left:$e,top:fe,width:X,height:H}=u.select,Ae=u.scales[F].ori,Qe=u.posToVal,ht,Te,et,Ft,Tt,rn=Z!=null&&R(Z,F),vn=N!=null&&A(N,ie);rn&&ft?(Ae==0?(ht=$e,Te=X):(ht=fe,Te=H),et=z[Z],Ft=V(Qe(ht,F),et,y,0),Tt=V(Qe(ht+Te,F),et,y,0),zr(yn(Ft,Tt),lt(Tt-Ft))):zr(0,y),vn&&mt?(Ae==1?(ht=$e,Te=X):(ht=fe,Te=H),et=z[N],Ft=ne(Qe(ht,ie),et,x,0),Tt=ne(Qe(ht+Te,ie),et,x,0),Tr(yn(Ft,Tt),lt(Tt-Ft))):Tr(0,x)}else Qs()}else{let Z=lt(qd-Fd),N=lt(Vd-Jd);if(O.ori==1){let ie=Z;Z=N,N=ie}ft=vt.x&&Z>=vt.dist,mt=vt.y&&N>=vt.dist;let R=vt.uni;R!=null?ft&&mt&&(ft=Z>=R,mt=N>=R,!ft&&!mt&&(N>Z?mt=!0:ft=!0)):vt.x&&vt.y&&(ft||mt)&&(ft=mt=!0);let A,F;ft&&(O.ori==0?(A=Bi,F=De):(A=Gi,F=Ve),zr(yn(A,F),lt(F-A)),mt||Tr(0,x)),mt&&(O.ori==1?(A=Bi,F=De):(A=Gi,F=Ve),Tr(yn(A,F),lt(F-A)),ft||zr(0,y)),!ft&&!mt&&(zr(0,0),Tr(0,0))}if(vt._x=ft,vt._y=mt,u==null){if(p){if(of!=null){let[Z,N]=it.scales;it.values[0]=Z!=null?zn(O.ori==0?De:Ve,Z):null,it.values[1]=N!=null?zn(O.ori==1?De:Ve,N):null}Pr(ol,r,De,Ve,le,L,g)}if(Ci){let Z=p&&it.setSeries,N=In.prox;di==null?Dn<=N&&xn(Hn,Yi,!0,Z):Dn>N?xn(null,Yi,!0,Z):Hn!=di&&xn(Hn,Yi,!0,Z)}}tn&&(de.idx=g,Ks()),d!==!1&&pt("setCursor")}let Bn=null;Object.defineProperty(r,"rect",{get(){return Bn==null&&Zr(!1),Bn}});function Zr(u=!1){u?Bn=null:(Bn=S.getBoundingClientRect(),pt("syncRect",Bn))}function Bd(u,d,p,g,_,y,x){W._lock||Vn&&u!=null&&u.movementX==0&&u.movementY==0||(Ys(u,d,p,g,_,y,x,!1,u!=null),u!=null?fi(null,!0,!0):fi(d,!0,!1))}function Ys(u,d,p,g,_,y,x,Z,N){if(Bn==null&&Zr(!1),ui(u),u!=null)p=u.clientX-Bn.left,g=u.clientY-Bn.top;else{if(p<0||g<0){De=-10,Ve=-10;return}let[R,A]=it.scales,F=d.cursor.sync,[ie,ke]=F.values,[$e,fe]=F.scales,[X,H]=it.match,Ae=d.axes[0].side%2==1,Qe=O.ori==0?le:L,ht=O.ori==1?le:L,Te=Ae?y:_,et=Ae?_:y,Ft=Ae?g:p,Tt=Ae?p:g;if($e!=null?p=X(R,$e)?s(ie,z[R],Qe,0):-10:p=Qe*(Ft/Te),fe!=null?g=H(A,fe)?s(ke,z[A],ht,0):-10:g=ht*(Tt/et),O.ori==1){let rn=p;p=g,g=rn}}N&&(d==null||d.cursor.event.type==ol)&&((p<=1||p>=le-1)&&(p=vi(p,le)),(g<=1||g>=L-1)&&(g=vi(g,L))),Z?(Fd=p,Jd=g,[Bi,Gi]=W.move(r,p,g)):(De=p,Ve=g)}const Xs={width:0,height:0,left:0,top:0};function Qs(){Vo(Xs,!1)}let Gd,Kd,Yd,Xd;function Qd(u,d,p,g,_,y,x){Vn=!0,ft=mt=vt._x=vt._y=!1,Ys(u,d,p,g,_,y,x,!0,!1),u!=null&&(zt(al,zl,ef,!1),Pr(Gf,r,Bi,Gi,le,L,null));let{left:Z,top:N,width:R,height:A}=Re;Gd=Z,Kd=N,Yd=R,Xd=A}function ef(u,d,p,g,_,y,x){Vn=vt._x=vt._y=!1,Ys(u,d,p,g,_,y,x,!1,!0);let{left:Z,top:N,width:R,height:A}=Re,F=R>0||A>0,ie=Gd!=Z||Kd!=N||Yd!=R||Xd!=A;if(F&&ie&&Vo(Re),vt.setScale&&F&&ie){let ke=Z,$e=R,fe=N,X=A;if(O.ori==1&&(ke=N,$e=A,fe=Z,X=R),ft&&Wn(C,zn(ke,C),zn(ke+$e,C)),mt)for(let H in z){let Ae=z[H];H!=C&&Ae.from==null&&Ae.min!=Ee&&Wn(H,zn(fe+X,H),zn(fe,H))}Qs()}else W.lock&&(W._lock=!W._lock,fi(d,!0,u!=null));u!=null&&(Di(al,zl),Pr(al,r,De,Ve,le,L,null))}function Ly(u,d,p,g,_,y,x){if(W._lock)return;ui(u);let Z=Vn;if(Vn){let N=!0,R=!0,A=10,F,ie;O.ori==0?(F=ft,ie=mt):(F=mt,ie=ft),F&&ie&&(N=De<=A||De>=le-A,R=Ve<=A||Ve>=L-A),F&&N&&(De=De<Bi?0:le),ie&&R&&(Ve=Ve<Gi?0:L),fi(null,!0,!0),Vn=!1}De=-10,Ve=-10,ze.fill(null),fi(null,!0,!0),Z&&(Vn=Z)}function tf(u,d,p,g,_,y,x){W._lock||(ui(u),js(),Qs(),u!=null&&Pr(Xf,r,De,Ve,le,L,null))}function nf(){T.forEach(xZ),Os(r.width,r.height,!0)}wi(wa,ir,nf);const Xi={};Xi.mousedown=Qd,Xi.mousemove=Bd,Xi.mouseup=ef,Xi.dblclick=tf,Xi.setSeries=(u,d,p,g)=>{let _=it.match[2];p=_(r,d,p),p!=-1&&xn(p,g,!0,!1)},xt&&(zt(Gf,S,Qd),zt(ol,S,Bd),zt(Kf,S,u=>{ui(u),Zr(!1)}),zt(Yf,S,Ly),zt(Xf,S,tf),jl.add(r),r.syncRect=Zr);const Wo=r.hooks=e.hooks||{};function pt(u,d,p){Ws?xr.push([u,d,p]):u in Wo&&Wo[u].forEach(g=>{g.call(null,r,d,p)})}(e.plugins||[]).forEach(u=>{for(let d in u.hooks)Wo[d]=(Wo[d]||[]).concat(u.hooks[d])});const rf=(u,d,p)=>p,it=rt({key:null,setSeries:!1,filters:{pub:um,sub:um},scales:[C,k[1]?k[1].scale:null],match:[cm,cm,rf],values:[null,null]},W.sync);it.match.length==2&&it.match.push(rf),W.sync=it;const of=it.key,el=q$(of);function Pr(u,d,p,g,_,y,x){it.filters.pub(u,d,p,g,_,y,x)&&el.pub(u,d,p,g,_,y,x)}el.sub(r);function My(u,d,p,g,_,y,x){it.filters.sub(u,d,p,g,_,y,x)&&Xi[u](null,d,p,g,_,y,x)}r.pub=My;function Fy(){el.unsub(r),jl.delete(r),ri.clear(),Pl(wa,ir,nf),c.remove(),Se?.remove(),pt("destroy")}r.destroy=Fy;function tl(){pt("init",e,t),Od(t||e.data,!1),K[C]?Hs(C,K[C]):js(),Co=Re.show&&(Re.width>0||Re.height>0),li=tn=!0,Os(e.width,e.height)}return k.forEach(Ud),T.forEach($y),i?i instanceof HTMLElement?(i.appendChild(c),tl()):i(r,tl):tl(),r}St.assign=rt;St.fmtNum=bd;St.rangeNum=Sa;St.rangeLog=xs;St.rangeAsinh=vd;St.orient=Ni;St.pxRatio=we;St.join=v6;St.fmtDate=yd,St.tzDate=T6;St.sync=q$;{St.addGap=cZ,St.clipGaps=Zs;let e=St.paths={points:K$};e.linear=X$,e.stepped=mZ,e.bars=pZ,e.spline=gZ}function Rl(e,t=0){return e<=0?[]:Array.from({length:e},(i,r)=>t+r+1)}function zZ(e,t){return e<=0&&t<=0?[]:[...Rl(e,0),...Rl(t,e)]}function ny(e,t){return t==="failed"||t==="cancelled"||e.kernel_complete===!0?!0:t==="completed"}function dl(e){return`${e} (panel)`}function TZ(e){const t=String(e.training_progress_phase??e.progress_phase??"").toLowerCase();if((t==="missing"?"":t)==="floop"||String(e.job_type??"").toLowerCase().includes("floop"))return!0;const n=e.run_config;return!!(n&&typeof n=="object"&&String(n.model??"").toLowerCase().includes("floop"))}function ZZ(e){const t=e.training_curve_source_batches,i=[],r=t&&typeof t.ordering=="number"?t.ordering:null,n=t&&typeof t.convergence=="number"?t.convergence:null,o=Array.isArray(e.ordering_errors)?e.ordering_errors.length:0,a=Array.isArray(e.convergence_errors)?e.convergence_errors.length:0;r!=null&&r>o&&i.push(`${o} of ${r.toLocaleString()} ordering batch samples`),n!=null&&n>a&&i.push(`${a} of ${n.toLocaleString()} convergence batch samples`);const s=e.te_evaluation,l=s?.te_panel_size,c=s?.te_panel_n_train;if(typeof l=="number"&&typeof c=="number"&&c>0){const $=s?.te_panel_stratified===!0?", stratified by mesh":"";i.push(`TE curve uses a fixed panel of ${l.toLocaleString()} of ${c.toLocaleString()} training rows${$}`)}const m=e.panel_topographic_error,f=e.map_topographic_error;return m!=null&&f!=null&&Number.isFinite(Number(m))&&Number.isFinite(Number(f))&&i.push(`Panel TE ${Number(m).toFixed(4)} · Map TE ${Number(f).toFixed(4)} (full training set)`),i.length===0?null:`${r!=null||n!=null?"Displaying uniformly subsampled QE+TE curves (≤1000 points/phase, joint indices): ":""}${i.join("; ")}.`.replace(/: ;/,":")}function fl(e,t){if(t<=0)return[];if(e.length===t)return e;if(e.length===0)return Array(t).fill(null);if(e.length>=t)return e.slice(0,t);const i=t-e.length;return[...Array(i).fill(null),...e]}function iy(e){return e==="completed"||e==="failed"||e==="cancelled"}function Td(e){const t=String(e.status??"");return t!=="completed"?iy(t):(e.finalize_job_id!=null&&String(e.finalize_job_id)!==""?String(e.finalize_job_id):null)?String(e.finalize_status??"")==="completed":!0}function Al(e){const t=String(e.status??"");if(!iy(t))return!0;if(t!=="completed"||e.finalize_failed===!0)return!1;const i=e.finalize_job_id!=null&&String(e.finalize_job_id)!==""?String(e.finalize_job_id):null,r=e.finalize_status!=null&&String(e.finalize_status)!==""?String(e.finalize_status):null;return i!=null&&r!=="completed"}const En=new ET({name:"Training Monitor",version:"1.0.0"},{},{autoResize:!0});let Um=!1,Nm=!1;const PZ=document.getElementById("progress-fill"),EZ=document.getElementById("progress-pct"),Cl=document.getElementById("elapsed"),UZ=document.getElementById("phase"),oa=document.getElementById("eta"),Ll=document.getElementById("job-id"),Dt=document.getElementById("status-badge"),NZ=document.getElementById("params"),Wt=document.getElementById("advisory-banner");document.getElementById("curve");const Do=document.getElementById("chart-target"),Si=document.getElementById("curve-fallback"),ry=document.getElementById("chart-legend"),OZ=document.getElementById("metric-scalars"),to=document.getElementById("cancel-btn"),hi=document.getElementById("open-results-btn"),nr=document.getElementById("copy-monitor-url"),Om=document.getElementById("open-standalone"),ml=document.getElementById("hitgrid-container"),aa=document.getElementById("hitgrid-canvas"),jZ=document.getElementById("hitgrid-meta"),Ml=5e3,pl=2e3;let tt="",hl=null,Jr=null,qr=null,gl=!1,jm=!1,Kn=null;function Zd(e){return`/viz/results-explorer?mode=standalone&job_id=${encodeURIComponent(e)}`}function oy(e){const t=String(e.id??e.job_id??tt);if(!t||!hi)return;hi.style.display="inline-flex";const i=String(e.status??"")==="completed"&&Td(e);hi.href=Zd(t),i?(hi.classList.remove("nav-btn-disabled"),hi.title="Browse figures for this job"):(hi.classList.add("nav-btn-disabled"),hi.title="Available when training and finalize (if any) complete")}async function DZ(e){if(jm||String(e.status??"")!=="completed"||!Td(e))return;jm=!0,oy(e);const t=String(e.id??e.job_id??tt);if(!t)return;const i=Zd(t);if(kr){window.open(i,"_blank","noopener,noreferrer");return}try{await En.callServerTool({name:"results_explorer",arguments:{job_id:t}})}catch{}}function Cr(e){if(e<60)return`${e}s`;const t=Math.floor(e/60),i=e%60;return t<60?`${t}m ${i}s`:`${Math.floor(t/60)}h ${t%60}m`}function RZ(e){if(Jr)return;hl=e;const t=()=>{if(hl==null)return;const i=Math.max(0,Math.round((Date.now()-hl)/1e3));Cl.textContent=Cr(i)};t(),Jr=setInterval(t,1e3)}function Vr(){Jr&&(clearInterval(Jr),Jr=null)}function AZ(e,t,i){const r=ny(e,t);r&&Vr();const n=e.training_elapsed_sec!=null?Number(e.training_elapsed_sec):e.kernel_elapsed_sec!=null?Number(e.kernel_elapsed_sec):null,o=e.wall_elapsed_sec!=null?Number(e.wall_elapsed_sec):null;if(n!=null&&n>=0)Cl.textContent=r?`Training ${Cr(Math.round(n))}`:Cr(Math.round(n));else if(!r&&e.started_at&&typeof e.started_at=="string"){const s=Date.parse(e.started_at);Number.isNaN(s)||RZ(s)}else e.training_duration_seconds&&(Cl.textContent=`${e.training_duration_seconds}s`);if(r){if(i&&o!=null&&o>=0){const s=n!=null?Math.round(n):null;oa.textContent=s==null||Math.abs(Math.round(o)-s)>=2?`Wall ${Cr(Math.round(o))}`:""}else oa.textContent="";return}const a=e.training_eta_sec!=null?Number(e.training_eta_sec):null;a!=null&&a>0?oa.textContent=`ETA ~${Cr(Math.round(a))}`:oa.textContent=""}function ay(){const e=Do.clientWidth;if(e>0)return Math.max(e,200);const t=En.getHostContext?.()?.containerDimensions,i=t?.width??t?.maxWidth;return Math.max(typeof i=="number"?i:360,200)}function Nn(e){return Array.isArray(e)?e.map(t=>Number(t)).filter(t=>Number.isFinite(t)):[]}function Fl(e){return Nn(e.ordering_errors).length>0||Nn(e.convergence_errors).length>0||Nn(e.ordering_topographic_errors).length>0||Nn(e.convergence_topographic_errors).length>0}function sa(e){return Array(e).fill(null)}function sy(){Kn&&(Kn.destroy(),Kn=null),Do.innerHTML=""}function CZ(e,t){ry.innerHTML=e.map((i,r)=>`<span class="legend-item"><span class="legend-swatch" style="background:${t[r]}"></span>${i}</span>`).join("")}function Dm(e,t,i="Batch sample"){if(sy(),e.length<2)return;Si.style.display="none";const r=getComputedStyle(document.documentElement).getPropertyValue("--accent").trim()||"#4f46e5",n=getComputedStyle(document.documentElement).getPropertyValue("--coral").trim()||"#f97316",o=getComputedStyle(document.documentElement).getPropertyValue("--text2").trim()||"#666",a=getComputedStyle(document.documentElement).getPropertyValue("--border").trim()||"#e0e0e0",s=t.map(f=>({...f,stroke:f.stroke==="accent"?r:f.stroke==="coral"?n:f.stroke})),l={width:ay(),height:160,cursor:{show:!0},select:{show:!1,left:0,top:0,width:0,height:0},scales:{x:{time:!1},y:{auto:!0},y2:{auto:!0}},axes:[{stroke:o,grid:{stroke:a,width:1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:i,labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:32},{scale:"y",stroke:o,grid:{stroke:a,width:1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:"QE",labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:48},{scale:"y2",side:1,stroke:o,grid:{show:!1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:"TE",labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:48}],series:[{},...s.map(f=>({label:f.label,scale:f.scale,stroke:f.stroke,width:f.scale==="y"?2:1.5,dash:f.dash,spanGaps:!1}))]},c=[e,...s.map(f=>f.values)];Kn=new St(l,c,Do),CZ(s.map(f=>f.label),s.map(f=>f.stroke));const m=ZZ(ly);m&&ry.insertAdjacentHTML("beforeend",`<div class="curve-source-note" style="margin-top:6px;font-size:11px;color:var(--text2);">${m}</div>`)}let ly={};function LZ(e,t){ny(e,t);const i=e;ly=i;const r=Nn(i.ordering_errors),n=Nn(i.convergence_errors),o=Nn(i.ordering_topographic_errors),a=Nn(i.convergence_topographic_errors);if(r.length===0&&n.length===0&&o.length===0&&a.length===0)return!1;const s=TZ(i),l=r.length,c=n.length,m=s?Math.max(l,o.length,1):Math.max(l+c,1),f=s?Rl(m):zZ(l,c);if(f.length<2)return!1;if(s){const J=r,k=fl(o,m),T=[{label:"QE (FLooP steps)",values:J,scale:"y",stroke:"accent"}];return o.length>0&&T.push({label:dl("TE (FLooP steps)"),values:k,scale:"y2",stroke:"accent",dash:[4,4]}),Dm(f,T,"FLooP step"),!0}const h=[...r,...sa(c)],$=[...sa(l),...n],S=[...fl(o,l),...sa(c)],U=[...sa(l),...fl(a,c)],D=[];return l>0&&D.push({label:"QE ordering",values:h,scale:"y",stroke:"accent"}),c>0&&D.push({label:"QE convergence",values:$,scale:"y",stroke:"coral"}),o.length>0&&D.push({label:dl("TE ordering"),values:S,scale:"y2",stroke:"accent",dash:[4,4]}),a.length>0&&D.push({label:dl("TE convergence"),values:U,scale:"y2",stroke:"coral",dash:[4,4]}),Dm(f,D,"Batch sample"),!0}function MZ(e){const t=Nn(e.hit_grid),i=Nn(e.hit_grid_dims);if(t.length===0||i.length<2){ml.style.display="none";return}const r=Math.max(1,Math.round(i[0])),n=Math.max(1,Math.round(i[1]));if(r*n!==t.length){ml.style.display="none";return}ml.style.display="block",aa.width=r,aa.height=n,aa.style.height=`${Math.min(220,n*8)}px`;const o=aa.getContext("2d");if(!o)return;const a=t.reduce((U,D)=>D>U?D:U,0)||1,s=getComputedStyle(document.documentElement).getPropertyValue("--accent").trim()||"#4f46e5",l=/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(s.replace("#","")),c=l?parseInt(l[1],16):79,m=l?parseInt(l[2],16):70,f=l?parseInt(l[3],16):229,h=o.createImageData(r,n);let $=0;for(let U=0;U<t.length;U++){const D=t[U]/a;t[U]>0&&$++;const J=U%r,T=(Math.floor(U/r)*r+J)*4;h.data[T]=Math.round(245-D*(245-c)),h.data[T+1]=Math.round(245-D*(245-m)),h.data[T+2]=Math.round(245-D*(245-f)),h.data[T+3]=255}o.putImageData(h,0,0);const S=t.reduce((U,D)=>U+D,0);jZ.textContent=`${r}×${n} nodes · ${$} occupied (${($/t.length*100).toFixed(0)}%) · ${S} sampled hits · max ${a}`}function FZ(e){const t=e.quantization_error??e.qe,i=e.panel_topographic_error??e.epoch_topographic_error??e.topographic_error,r=e.map_topographic_error,n=[];t!=null&&Number.isFinite(Number(t))&&n.push(`QE ${Number(t).toFixed(4)}`),i!=null&&Number.isFinite(Number(i))&&n.push(`Panel TE ${Number(i).toFixed(4)}`),r!=null&&Number.isFinite(Number(r))&&n.push(`Map TE ${Number(r).toFixed(4)}`),OZ.textContent=n.length>0?n.join(" · "):""}function uy(e){return e==="completed"||e==="failed"||e==="cancelled"}function cy(e){const t=e.standaloneUrl;if(t)return t;if(kr&&tt)return`${window.location.origin}/viz/training-monitor?mode=standalone&job_id=${encodeURIComponent(tt)}`}function JZ(e){const t=e.relatedUrls;if(t?.resultsExplorer)return t.resultsExplorer;if(kr&&tt)return`${window.location.origin}/viz/results-explorer?mode=standalone&job_id=${encodeURIComponent(tt)}`}function qZ(e){const t=cy(e);t&&(nr.style.display="inline-block",nr.dataset.url=t,Om.href=t,Om.style.display="inline-block")}function VZ(e,t,i){if(i||t!=="completed")return;const r=JZ(e);r&&(Wt.style.display="block",Wt.innerHTML=`Training complete. <a href="${r}" target="_blank" rel="noreferrer">Open results explorer</a> to browse figures.`)}nr.addEventListener("click",async()=>{const e=nr.dataset.url||cy({standaloneUrl:void 0});if(e)try{await navigator.clipboard.writeText(e),nr.textContent="Copied!",setTimeout(()=>{nr.textContent="Copy monitor URL"},1500)}catch{window.prompt("Copy monitor URL:",e)}});function WZ(e,t){if(kr||Um||Nm||!Td(e))return;const i=String(e.id??e.job_id??tt);if(!i)return;Um=!0;const r=[`Training job ${i} ${t}.`],n=e.quantization_error??e.qe,o=e.map_topographic_error??e.topographic_error,a=e.panel_topographic_error??e.epoch_topographic_error;if(n!=null&&Number.isFinite(Number(n))&&r.push(`Final QE: ${Number(n).toFixed(4)}`),a!=null&&Number.isFinite(Number(a))&&r.push(`Final panel TE: ${Number(a).toFixed(4)}`),o!=null&&Number.isFinite(Number(o))&&r.push(`Final map TE: ${Number(o).toFixed(4)}`),e.finalize_failed===!0)r.push(`Finalize failed${e.finalize_failure_stage?` [${String(e.finalize_failure_stage)}]`:""}: ${String(e.finalize_error??"results not uploaded")}. Results may be unavailable until finalize is retried.`);else if(t==="completed"){r.push(`Use results(get, job_id="${i}") for metrics, then results_explorer(job_id="${i}") to browse figures.`);const s=Zd(i);r.push(`Results explorer: ${s}`)}DZ(e),En.updateModelContext({content:[{type:"text",text:r.join(`
143
143
  `)}]}).catch(()=>{Nm=!0})}async function HZ(e){const t=String(e.status??"");if(!uy(t)||Fl(e)||gl)return e;const i=String(e.id??e.job_id??tt);if(!i)return e;gl=!0;try{if(kr){const o=await fetch(`/api/results/${i}/training-log`);if(!o.ok)return e;const a=await o.json();return{...e,...a}}const n=(await En.callServerTool({name:"training_monitor",arguments:{job_id:i,fetch_training_log:!0}})).structuredContent;if(n&&typeof n=="object")return{...e,...n}}catch{gl=!1}return e}function xa(){qr&&(clearTimeout(qr),qr=null)}function Jl(e){xa(),qr=setTimeout(()=>{qr=null,BZ()},e)}async function BZ(){if(tt)try{const e=await En.callServerTool({name:"training_monitor",arguments:{job_id:tt}});await dy(e)}catch{Jl(Ml)}}function dy(e){let t;const i=e.content?.find(o=>o.type==="image");i&&(t=`data:${i.mimeType};base64,${i.data}`);const r=e.structuredContent;if(r&&typeof r=="object"&&r.type==="training-monitor"){if(ql(r,t),String(r.status??"running"),Al(r)){const o=typeof r.refresh_interval_ms=="number"?r.refresh_interval_ms:Ml;Jl(o)}else xa();return}const n=e.content?.find(o=>o.type==="text");if(n?.type==="text"&&n.text)try{const o=JSON.parse(n.text);ql(o,t),Al(o)?Jl(Ml):xa()}catch{t&&(Si.style.display="none",Do.innerHTML=`<img src="${t}" alt="Learning Curve" style="max-width:100%;border-radius:4px;" />`)}}function Rm(e){const t=[];if(e.grid){const i=e.grid;t.push(["Grid",`${i[0]}×${i[1]}`])}if(e.model&&t.push(["Model",String(e.model)]),e.epochs){const i=e.epochs;t.push(["Epochs",Array.isArray(i)?`${i[0]}+${i[1]}`:String(i)])}e.n_features&&t.push(["Features",String(e.n_features)]),e.n_samples&&t.push(["Samples",String(e.n_samples)]),e.dataset_rows&&t.push(["Dataset rows",Number(e.dataset_rows).toLocaleString()]),e.dataset_name&&t.push(["Dataset",String(e.dataset_name)]),e.backend&&t.push(["Backend",String(e.backend)]),e.attempt!=null&&Number(e.attempt)>1&&t.push(["Attempt",String(e.attempt)]),NZ.innerHTML=t.map(([i,r])=>`<div class="param"><span class="param-label">${i}: </span><span class="param-value">${r}</span></div>`).join("")}async function ql(e,t){let i=e;uy(String(e.status??""))&&!Fl(e)&&(i=await HZ(e)),i.id&&(tt=i.id),i.job_id&&(tt=i.job_id),Ll.textContent=tt;let r=i.status??"running";const n=i.finalize_failed===!0,o=i.finalize_job_id!=null&&String(i.finalize_job_id)!==""?String(i.finalize_job_id):null,a=i.finalize_status!=null&&String(i.finalize_status)!==""?String(i.finalize_status):null;r==="completed"&&n?(Dt.textContent="training done · finalize failed",Dt.className="status-badge status-failed",to.style.display="none",Vr(),Wt.style.display="block",Wt.textContent=`Finalize job ${o??"unknown"} failed`+(i.finalize_failure_stage?` [${String(i.finalize_failure_stage)}]`:"")+`: ${String(i.finalize_error??"results not uploaded")}. Poll jobs(status) or retry finalize before results(get).`):r==="completed"&&o&&a!=="completed"?(Dt.textContent="training done · finalizing",Dt.className="status-badge status-warning",Vr(),Wt.style.display="block",Wt.textContent=`Finalize job ${o} (${a??"running"}). Wait for finalize before results(get).`):(Dt.textContent=r,Dt.className=`status-badge status-${r==="completed"?"completed":r==="failed"||r==="cancelled"?"failed":"running"}`,(r==="completed"||r==="failed"||r==="cancelled")&&(to.style.display="none",Vr()));const s=r==="completed"&&n||r==="completed"&&o!=null&&a!=="completed",l=(i.progress??0)*100;PZ.style.width=`${l}%`,EZ.textContent=`${l.toFixed(1)}%`;const c=i.progress_phase!=null&&String(i.progress_phase)!==""?String(i.progress_phase):r==="running"?"preprocessing (pending)":"—";UZ.textContent=c,AZ(i,r,s);const m=i.dataset_rows!=null?Number(i.dataset_rows):null,f=r==="running"&&l<5&&(c.startsWith("download")||c.startsWith("preprocessing")||c.includes("pending"));!s&&f&&m!=null&&m>1e6?(Wt.style.display="block",Wt.textContent=`Loading/normalizing ${m.toLocaleString()} rows — progress may stay near 0% until ingest completes.`):!s&&f&&l<1?(Wt.style.display="block",Wt.textContent="Early preprocessing (download/parse/normalize) — epoch counter starts after ingest completes."):!s&&r==="completed"?VZ(i,r,s):s||(Wt.style.display="none"),Rm(i),i.summary&&Rm(i.summary),oy(i),qZ(i),FZ(i),MZ(i),WZ(i,r),Fl(i)?LZ(i,r):t?(sy(),Si.style.display="none",Do.innerHTML=`<img src="${t}" alt="Learning Curve" style="max-width:100%;border-radius:4px;" />`):Kn||(Si.style.display="block",Si.textContent="Waiting for training data...")}function Am(){const e=new Date().toISOString().replace("T"," ").slice(0,19);Dt.textContent=`cancelled at ${e} UTC`,Dt.className="status-badge status-failed",to.style.display="none",Vr(),xa()}const fy=new URLSearchParams(window.location.search),kr=fy.get("mode")==="standalone",vl=fy.get("job_id");if(kr&&vl){tt=vl,Ll.textContent=vl,to.addEventListener("click",async()=>{if(tt)try{(await fetch(`/api/jobs/${tt}/cancel`,{method:"POST"})).ok?Am():Dt.textContent="cancel failed"}catch{Dt.textContent="cancel failed"}});const e=5;let t=0;const i=o=>{Wt.style.display="block",Wt.textContent=o},r="The local viz port is assigned per MCP session and changes when the proxy restarts. Re-run training_monitor for a fresh URL, or set BARIVIA_VIZ_PORT in your MCP env for a persistent port.",n=async()=>{try{const o=await fetch(`/api/jobs/${tt}`);if(!o.ok){if(t++,o.status===404){i(`Job ${tt} was not found via the local viz server (HTTP 404). It may have been deleted, or this link points at a stale session. ${r}`);return}if(t>=e){i(`Could not load job status (HTTP ${o.status}) after ${t} attempts. ${r}`);return}setTimeout(n,pl);return}t=0;const a=await o.json();await ql(a),Al(a)&&setTimeout(n,pl)}catch{if(t++,t>=e){i(`Cannot reach the local viz server (network error). The MCP proxy may have stopped or restarted. ${r}`);return}setTimeout(n,pl)}};n()}else to.addEventListener("click",async()=>{if(tt)try{await En.callServerTool({name:"jobs",arguments:{action:"cancel",job_id:tt}}),Am(),En.updateModelContext({content:[{type:"text",text:`User cancelled job ${tt}`}]})}catch{Dt.textContent="cancel failed"}}),En.ontoolinput=e=>{const t=e?.arguments??{},i=t.job_id!=null?String(t.job_id):"";i&&(tt=i,Ll.textContent=i),Dt.textContent||(Dt.textContent="loading",Dt.className="status-badge status-running"),Kn||(Si.style.display="block",Si.textContent="Loading training data...")},En.ontoolresult=e=>{dy(e)},En.connect();window.addEventListener("resize",()=>{Kn&&Kn.setSize({width:ay(),height:160})});</script>
144
144
  <style rel="stylesheet" crossorigin>.uplot,.uplot *,.uplot *:before,.uplot *:after{box-sizing:border-box}.uplot{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5;width:min-content}.u-title{text-align:center;font-size:18px;font-weight:700}.u-wrap{position:relative;-webkit-user-select:none;user-select:none}.u-over,.u-under{position:absolute}.u-under{overflow:hidden}.uplot canvas{display:block;position:relative;width:100%;height:100%}.u-axis{position:absolute}.u-legend{font-size:14px;margin:auto;text-align:center}.u-inline{display:block}.u-inline *{display:inline-block}.u-inline tr{margin-right:16px}.u-legend th{font-weight:600}.u-legend th>*{vertical-align:middle;display:inline-block}.u-legend .u-marker{width:1em;height:1em;margin-right:4px;background-clip:padding-box!important}.u-inline.u-live th:after{content:":";vertical-align:middle}.u-inline:not(.u-live) .u-value{display:none}.u-series>*{padding:4px}.u-series th{cursor:pointer}.u-legend .u-off>*{opacity:.3}.u-select{background:#00000012;position:absolute;pointer-events:none}.u-cursor-x,.u-cursor-y{position:absolute;left:0;top:0;pointer-events:none;will-change:transform}.u-hz .u-cursor-x,.u-vt .u-cursor-y{height:100%;border-right:1px dashed #607D8B}.u-hz .u-cursor-y,.u-vt .u-cursor-x{width:100%;border-bottom:1px dashed #607D8B}.u-cursor-pt{position:absolute;top:0;left:0;border-radius:50%;border:0 solid;pointer-events:none;will-change:transform;background-clip:padding-box!important}.u-axis.u-off,.u-select.u-off,.u-cursor-x.u-off,.u-cursor-y.u-off,.u-cursor-pt.u-off{display:none}</style>
145
145
  </head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barivia/barsom-mcp",
3
- "version": "0.23.5",
3
+ "version": "0.23.8",
4
4
  "description": "barSOM MCP proxy — connect any MCP client to the barSOM cloud API for Self-Organizing Map analytics",
5
5
  "keywords": [
6
6
  "mcp",