dsh-context 0.53.1 → 0.53.3
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/lib/client.js +343 -343
- package/lib/index.js +81 -12
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -3400,11 +3400,12 @@ window.__ModuleLoader__.load({
|
|
|
3400
3400
|
}
|
|
3401
3401
|
//#endregion
|
|
3402
3402
|
//#region src/client/format.ts
|
|
3403
|
-
/** `fmt`: the k/M suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
|
|
3403
|
+
/** `fmt`: the k/M/B suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
|
|
3404
3404
|
function fmt(n) {
|
|
3405
3405
|
if (n === void 0 || n === null || isNaN(n)) return "—";
|
|
3406
3406
|
const sign = n < 0 ? "-" : "";
|
|
3407
3407
|
const a = Math.abs(n);
|
|
3408
|
+
if (a >= 1e9) return sign + (a / 1e9).toFixed(1) + "B";
|
|
3408
3409
|
if (a >= 1e6) return sign + (a / 1e6).toFixed(1) + "M";
|
|
3409
3410
|
if (a >= 1e3) return sign + (a / 1e3).toFixed(1) + "k";
|
|
3410
3411
|
return sign + String(Math.round(a));
|
|
@@ -5293,77 +5294,67 @@ window.__ModuleLoader__.load({
|
|
|
5293
5294
|
};
|
|
5294
5295
|
}
|
|
5295
5296
|
//#endregion
|
|
5296
|
-
//#region
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
* Build the address of a file read through one Session.
|
|
5315
|
-
* @param sessionId - the Session whose Host workspace resolves the path.
|
|
5316
|
-
* @param path - absolute or workspace-relative path; backslashes are normalized to `/`, and leading `./` prefixes are dropped.
|
|
5317
|
-
* @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
|
|
5318
|
-
*/
|
|
5319
|
-
function sessionFileAddress(sessionId, path) {
|
|
5320
|
-
const normalized = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "");
|
|
5321
|
-
return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`;
|
|
5322
|
-
}
|
|
5323
|
-
/**
|
|
5324
|
-
* Browser-safe Workspace path and display helpers.
|
|
5325
|
-
* @module @deepseek-ai/dsh-util-workspace-path
|
|
5326
|
-
*/
|
|
5327
|
-
/** Whether a path uses a Windows drive or UNC prefix. */
|
|
5328
|
-
function isWindowsStylePath(value) {
|
|
5329
|
-
return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
|
|
5330
|
-
}
|
|
5331
|
-
/**
|
|
5332
|
-
* Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
|
|
5333
|
-
* @param path - the path to classify.
|
|
5334
|
-
* @returns `true` for an absolute path; `false` for a Workspace-relative one.
|
|
5335
|
-
*/
|
|
5336
|
-
function isAbsoluteWorkspacePath(path) {
|
|
5337
|
-
return path.startsWith("/") || isWindowsStylePath(path);
|
|
5338
|
-
}
|
|
5339
|
-
/**
|
|
5340
|
-
* Read the final non-empty segment of a Workspace path for display.
|
|
5341
|
-
* Workspace-label surfaces use this helper instead of deriving another basename.
|
|
5342
|
-
* @param path - Workspace directory path using POSIX or Windows separators.
|
|
5343
|
-
* @returns the final segment, or an empty string for a separator-only path.
|
|
5344
|
-
*/
|
|
5345
|
-
function workspaceTitleOf(path) {
|
|
5346
|
-
const trimmed = path.replace(/[/\\]+$/, "");
|
|
5347
|
-
const separator = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
|
|
5348
|
-
return trimmed.slice(separator + 1);
|
|
5297
|
+
//#region src/client/overviewStore.ts
|
|
5298
|
+
function createStore() {
|
|
5299
|
+
let open = false;
|
|
5300
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
5301
|
+
return {
|
|
5302
|
+
subscribe: (listener) => {
|
|
5303
|
+
listeners.add(listener);
|
|
5304
|
+
return () => {
|
|
5305
|
+
listeners.delete(listener);
|
|
5306
|
+
};
|
|
5307
|
+
},
|
|
5308
|
+
getSnapshot: () => open,
|
|
5309
|
+
set: (next) => {
|
|
5310
|
+
if (next === open) return;
|
|
5311
|
+
open = next;
|
|
5312
|
+
for (const listener of listeners) listener();
|
|
5313
|
+
}
|
|
5314
|
+
};
|
|
5349
5315
|
}
|
|
5316
|
+
const overviewStore = createStore();
|
|
5317
|
+
//#endregion
|
|
5318
|
+
//#region src/client/components/overviewButton.tsx
|
|
5350
5319
|
/**
|
|
5351
|
-
* The
|
|
5352
|
-
*
|
|
5353
|
-
*
|
|
5354
|
-
*
|
|
5355
|
-
*
|
|
5356
|
-
*
|
|
5357
|
-
*
|
|
5358
|
-
*
|
|
5320
|
+
* The Context Dashboard's sidebar entry: a footer action stacked directly
|
|
5321
|
+
* above Settings (the harness's own foot layout: footer actions, then the
|
|
5322
|
+
* settings row), mirroring the Settings trigger's geometry in both column
|
|
5323
|
+
* widths. The button carries the plugin emblem and, on the wide
|
|
5324
|
+
* column, its label. Clicking opens the overview overlay through the shared
|
|
5325
|
+
* module store (overviewStore.ts).
|
|
5326
|
+
*
|
|
5327
|
+
* The per-user `insightsEntry` preference takes the entry down (renders
|
|
5328
|
+
* null) without unregistering the seat. The subscription fails open: an
|
|
5329
|
+
* absent settings store, an unserved namespace, or a value the plugin
|
|
5330
|
+
* cannot understand all leave the entry visible.
|
|
5359
5331
|
*/
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5332
|
+
/** Stable subscription faces for the settings-less degrade (no re-subscribes). */
|
|
5333
|
+
const subscribeNever = () => () => {};
|
|
5334
|
+
const entryShown = () => "show";
|
|
5335
|
+
function makeOverviewButton(kit, settings) {
|
|
5336
|
+
const { t } = kit;
|
|
5337
|
+
const subscribeEntry = settings === void 0 ? subscribeNever : (listener) => settings.store.subscribe(listener);
|
|
5338
|
+
const getEntry = settings === void 0 ? entryShown : () => settings.insightsEntry();
|
|
5339
|
+
return function OverviewButton(props) {
|
|
5340
|
+
if ((0, react.useSyncExternalStore)(subscribeEntry, getEntry) === "hide") return null;
|
|
5341
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5342
|
+
type: "button",
|
|
5343
|
+
className: props.wide === true ? "lc-ov-entry" : "lc-ov-entry lc-ov-entry-rail",
|
|
5344
|
+
title: t("ov.entry"),
|
|
5345
|
+
"aria-label": t("ov.entry"),
|
|
5346
|
+
onClick: () => {
|
|
5347
|
+
overviewStore.set(true);
|
|
5348
|
+
},
|
|
5349
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextIcon, {
|
|
5350
|
+
size: props.wide === true ? 16 : 18,
|
|
5351
|
+
className: "lc-ov-entry-icon"
|
|
5352
|
+
}), props.wide === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5353
|
+
className: "lc-ov-entry-label",
|
|
5354
|
+
children: t("ov.entry")
|
|
5355
|
+
})]
|
|
5356
|
+
});
|
|
5357
|
+
};
|
|
5367
5358
|
}
|
|
5368
5359
|
//#endregion
|
|
5369
5360
|
//#region src/shared/providers.ts
|
|
@@ -5543,6 +5534,258 @@ window.__ModuleLoader__.load({
|
|
|
5543
5534
|
return formatCost(amount, currency).replace(/0+$/, "").replace(/\.$/, "");
|
|
5544
5535
|
}
|
|
5545
5536
|
//#endregion
|
|
5537
|
+
//#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/error.js
|
|
5538
|
+
/**
|
|
5539
|
+
* The only error thrown by the models.dev client.
|
|
5540
|
+
*
|
|
5541
|
+
* - `Transport` — the fetch itself failed (network, DNS, abort). `cause` is the underlying error.
|
|
5542
|
+
* - `UnexpectedStatus` — non-2xx response. `cause` is `{ status: number }`.
|
|
5543
|
+
* - `MalformedResponse` — the body was empty or not valid JSON. `cause` is the parse error, if any.
|
|
5544
|
+
*/
|
|
5545
|
+
var ModelsDevError = class extends Error {
|
|
5546
|
+
reason;
|
|
5547
|
+
name = "ModelsDevError";
|
|
5548
|
+
constructor(reason, options) {
|
|
5549
|
+
super(reason, options);
|
|
5550
|
+
this.reason = reason;
|
|
5551
|
+
}
|
|
5552
|
+
};
|
|
5553
|
+
//#endregion
|
|
5554
|
+
//#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/client.js
|
|
5555
|
+
/**
|
|
5556
|
+
* Creates a stateless models.dev client. Every method performs exactly one
|
|
5557
|
+
* `GET` and nothing is ever cached — callers who want caching should wrap
|
|
5558
|
+
* calls with their own policy. For a no-network alternative, see the
|
|
5559
|
+
* `@opencode-ai/models/snapshot` entrypoint.
|
|
5560
|
+
*/
|
|
5561
|
+
function make(options = {}) {
|
|
5562
|
+
const baseUrl = options.baseUrl ?? "https://models.dev";
|
|
5563
|
+
const base = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
|
|
5564
|
+
const request = async (path, requestOptions) => {
|
|
5565
|
+
const fetch = options.fetch ?? globalThis.fetch;
|
|
5566
|
+
const headers = new Headers();
|
|
5567
|
+
for (const [key, value] of new Headers(options.headers)) headers.set(key, value);
|
|
5568
|
+
for (const [key, value] of new Headers(requestOptions?.headers)) headers.set(key, value);
|
|
5569
|
+
let response;
|
|
5570
|
+
try {
|
|
5571
|
+
response = await fetch(new URL(path, base), {
|
|
5572
|
+
method: "GET",
|
|
5573
|
+
headers,
|
|
5574
|
+
signal: requestOptions?.signal
|
|
5575
|
+
});
|
|
5576
|
+
} catch (cause) {
|
|
5577
|
+
throw new ModelsDevError("Transport", { cause });
|
|
5578
|
+
}
|
|
5579
|
+
if (!response.ok) {
|
|
5580
|
+
try {
|
|
5581
|
+
await response.body?.cancel();
|
|
5582
|
+
} catch {}
|
|
5583
|
+
throw new ModelsDevError("UnexpectedStatus", { cause: { status: response.status } });
|
|
5584
|
+
}
|
|
5585
|
+
let text;
|
|
5586
|
+
try {
|
|
5587
|
+
text = await response.text();
|
|
5588
|
+
} catch (cause) {
|
|
5589
|
+
throw new ModelsDevError("Transport", { cause });
|
|
5590
|
+
}
|
|
5591
|
+
if (text === "") throw new ModelsDevError("MalformedResponse");
|
|
5592
|
+
try {
|
|
5593
|
+
return JSON.parse(text);
|
|
5594
|
+
} catch (cause) {
|
|
5595
|
+
throw new ModelsDevError("MalformedResponse", { cause });
|
|
5596
|
+
}
|
|
5597
|
+
};
|
|
5598
|
+
return {
|
|
5599
|
+
/** All providers with their models, pricing, and limits (`/api.json`). */
|
|
5600
|
+
providers: (requestOptions) => request("api.json", requestOptions),
|
|
5601
|
+
/** Provider-agnostic model metadata (`/models.json`). */
|
|
5602
|
+
models: (requestOptions) => request("models.json", requestOptions),
|
|
5603
|
+
/** Providers and model metadata in a single request (`/catalog.json`). */
|
|
5604
|
+
catalog: (requestOptions) => request("catalog.json", requestOptions)
|
|
5605
|
+
};
|
|
5606
|
+
}
|
|
5607
|
+
//#endregion
|
|
5608
|
+
//#region src/client/modelPrices.ts
|
|
5609
|
+
/**
|
|
5610
|
+
* The client's model-price book (client/cost.ts prices from it): the
|
|
5611
|
+
* models.dev registry, fetched through the official @opencode-ai/models
|
|
5612
|
+
* SDK. The registry payload is untrusted wire input, so `pricesBookOf`
|
|
5613
|
+
* re-proves every field at the boundary — a non-conforming provider/model/
|
|
5614
|
+
* cost entry drops whole. The book keeps EVERY provider that prices (keyed
|
|
5615
|
+
* by the registry's own provider id), so a dsh provider id outside the
|
|
5616
|
+
* rename table still prices by direct passthrough. One fetch per page load,
|
|
5617
|
+
* kicked on first subscribe; a failure degrades to a visible state the cost
|
|
5618
|
+
* cell notes, with a backed-off automatic retry — never a spinner, and
|
|
5619
|
+
* never an unhandled rejection.
|
|
5620
|
+
*/
|
|
5621
|
+
/** One finite non-negative registry figure, or null. */
|
|
5622
|
+
function rateOf(value) {
|
|
5623
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
5624
|
+
}
|
|
5625
|
+
/**
|
|
5626
|
+
* Extract the whole registry's per-model USD rates from a delivered
|
|
5627
|
+
* providers payload (`/api.json`), keyed by the registry's provider ids.
|
|
5628
|
+
* Any shape failure skips just that entry; a payload that is not a record
|
|
5629
|
+
* at all returns null (the store treats it as a failed fetch and retries).
|
|
5630
|
+
*/
|
|
5631
|
+
function pricesBookOf(value) {
|
|
5632
|
+
const data = asRecord(value);
|
|
5633
|
+
if (data === null || Array.isArray(data)) return null;
|
|
5634
|
+
const book = {};
|
|
5635
|
+
for (const providerId of Object.keys(data)) {
|
|
5636
|
+
const provider = asRecord(data[providerId]);
|
|
5637
|
+
const modelList = provider !== null ? asRecord(provider.models) : null;
|
|
5638
|
+
if (modelList === null) continue;
|
|
5639
|
+
const models = {};
|
|
5640
|
+
for (const id of Object.keys(modelList)) {
|
|
5641
|
+
const model = asRecord(modelList[id]);
|
|
5642
|
+
const cost = model !== null ? asRecord(model.cost) : null;
|
|
5643
|
+
if (cost === null) continue;
|
|
5644
|
+
const miss = rateOf(cost.input);
|
|
5645
|
+
const out = rateOf(cost.output);
|
|
5646
|
+
if (miss === null || out === null) continue;
|
|
5647
|
+
models[id] = {
|
|
5648
|
+
hit: rateOf(cost.cache_read) ?? miss,
|
|
5649
|
+
miss,
|
|
5650
|
+
write: rateOf(cost.cache_write) ?? miss,
|
|
5651
|
+
out
|
|
5652
|
+
};
|
|
5653
|
+
}
|
|
5654
|
+
if (Object.keys(models).length > 0) book[providerId] = models;
|
|
5655
|
+
}
|
|
5656
|
+
return book;
|
|
5657
|
+
}
|
|
5658
|
+
const defaultLoader = () => make().providers();
|
|
5659
|
+
/** The retry backoff base; each consecutive failure doubles the wait, capped at 3 doublings. */
|
|
5660
|
+
const RETRY_BASE_MS = 3e4;
|
|
5661
|
+
let loader = defaultLoader;
|
|
5662
|
+
let snap = {
|
|
5663
|
+
prices: null,
|
|
5664
|
+
failed: false
|
|
5665
|
+
};
|
|
5666
|
+
let inFlight = false;
|
|
5667
|
+
let failures = 0;
|
|
5668
|
+
let timer = null;
|
|
5669
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
5670
|
+
function fail() {
|
|
5671
|
+
snap = {
|
|
5672
|
+
...snap,
|
|
5673
|
+
failed: true
|
|
5674
|
+
};
|
|
5675
|
+
failures++;
|
|
5676
|
+
timer = setTimeout(() => {
|
|
5677
|
+
timer = null;
|
|
5678
|
+
fire();
|
|
5679
|
+
}, RETRY_BASE_MS * 2 ** Math.min(failures - 1, 3));
|
|
5680
|
+
}
|
|
5681
|
+
async function fire() {
|
|
5682
|
+
inFlight = true;
|
|
5683
|
+
try {
|
|
5684
|
+
const book = pricesBookOf(await loader());
|
|
5685
|
+
if (book !== null) {
|
|
5686
|
+
snap = {
|
|
5687
|
+
prices: book,
|
|
5688
|
+
failed: false
|
|
5689
|
+
};
|
|
5690
|
+
failures = 0;
|
|
5691
|
+
} else fail();
|
|
5692
|
+
} catch {
|
|
5693
|
+
fail();
|
|
5694
|
+
}
|
|
5695
|
+
inFlight = false;
|
|
5696
|
+
for (const fn of listeners) fn();
|
|
5697
|
+
}
|
|
5698
|
+
function kick() {
|
|
5699
|
+
if (snap.prices !== null || inFlight || timer !== null) return;
|
|
5700
|
+
fire();
|
|
5701
|
+
}
|
|
5702
|
+
/** The useSyncExternalStore seam: subscribing also kicks the first fetch. */
|
|
5703
|
+
const subscribeModelPrices = (fn) => {
|
|
5704
|
+
listeners.add(fn);
|
|
5705
|
+
kick();
|
|
5706
|
+
return () => {
|
|
5707
|
+
listeners.delete(fn);
|
|
5708
|
+
};
|
|
5709
|
+
};
|
|
5710
|
+
const getModelPricesSnap = () => snap;
|
|
5711
|
+
/** The stats board's read of the price book (null until the fetch lands). */
|
|
5712
|
+
function useModelPrices() {
|
|
5713
|
+
return (0, react.useSyncExternalStore)(subscribeModelPrices, getModelPricesSnap);
|
|
5714
|
+
}
|
|
5715
|
+
//#endregion
|
|
5716
|
+
//#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.1.5-rc.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
|
|
5717
|
+
/**
|
|
5718
|
+
* The `dsh-resource://file/…` address grammar: how a file is named across the
|
|
5719
|
+
* Sidebar and the resource model, built and parsed without touching a
|
|
5720
|
+
* filesystem.
|
|
5721
|
+
* @module
|
|
5722
|
+
*/
|
|
5723
|
+
/** The scheme and type every file address opens with. */
|
|
5724
|
+
const FILE_ADDRESS_PREFIX = "dsh-resource://file/";
|
|
5725
|
+
/** Component-encode one id or path segment, keeping `:` literal for drive letters. */
|
|
5726
|
+
function encodeSegment(segment) {
|
|
5727
|
+
return encodeURIComponent(segment).replace(/%3A/gi, ":");
|
|
5728
|
+
}
|
|
5729
|
+
/** Encode a `/`-separated path segment by segment. */
|
|
5730
|
+
function encodePath(path) {
|
|
5731
|
+
return path.split("/").map(encodeSegment).join("/");
|
|
5732
|
+
}
|
|
5733
|
+
/**
|
|
5734
|
+
* Build the address of a file read through one Session.
|
|
5735
|
+
* @param sessionId - the Session whose Host workspace resolves the path.
|
|
5736
|
+
* @param path - absolute or workspace-relative path; backslashes are normalized to `/`, and leading `./` prefixes are dropped.
|
|
5737
|
+
* @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
|
|
5738
|
+
*/
|
|
5739
|
+
function sessionFileAddress(sessionId, path) {
|
|
5740
|
+
const normalized = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "");
|
|
5741
|
+
return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`;
|
|
5742
|
+
}
|
|
5743
|
+
/**
|
|
5744
|
+
* Browser-safe Workspace path and display helpers.
|
|
5745
|
+
* @module @deepseek-ai/dsh-util-workspace-path
|
|
5746
|
+
*/
|
|
5747
|
+
/** Whether a path uses a Windows drive or UNC prefix. */
|
|
5748
|
+
function isWindowsStylePath(value) {
|
|
5749
|
+
return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
|
|
5750
|
+
}
|
|
5751
|
+
/**
|
|
5752
|
+
* Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
|
|
5753
|
+
* @param path - the path to classify.
|
|
5754
|
+
* @returns `true` for an absolute path; `false` for a Workspace-relative one.
|
|
5755
|
+
*/
|
|
5756
|
+
function isAbsoluteWorkspacePath(path) {
|
|
5757
|
+
return path.startsWith("/") || isWindowsStylePath(path);
|
|
5758
|
+
}
|
|
5759
|
+
/**
|
|
5760
|
+
* Read the final non-empty segment of a Workspace path for display.
|
|
5761
|
+
* Workspace-label surfaces use this helper instead of deriving another basename.
|
|
5762
|
+
* @param path - Workspace directory path using POSIX or Windows separators.
|
|
5763
|
+
* @returns the final segment, or an empty string for a separator-only path.
|
|
5764
|
+
*/
|
|
5765
|
+
function workspaceTitleOf(path) {
|
|
5766
|
+
const trimmed = path.replace(/[/\\]+$/, "");
|
|
5767
|
+
const separator = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
|
|
5768
|
+
return trimmed.slice(separator + 1);
|
|
5769
|
+
}
|
|
5770
|
+
/**
|
|
5771
|
+
* The address for a path as a caller holds it: a relative path, or an absolute
|
|
5772
|
+
* path inside the Session's workspace, becomes a `session`-scoped address; an
|
|
5773
|
+
* absolute path outside it, or one whose workspace root is unknown, keeps its
|
|
5774
|
+
* absolute path in that Session's address.
|
|
5775
|
+
* @param sessionId - the Session the path is read in.
|
|
5776
|
+
* @param cwd - that Session's workspace root, when known.
|
|
5777
|
+
* @param path - absolute or workspace-relative path, in either separator spelling.
|
|
5778
|
+
* @returns the `dsh-resource://file/…` address.
|
|
5779
|
+
*/
|
|
5780
|
+
function fileAddressFor(sessionId, cwd, path) {
|
|
5781
|
+
const normalized = path.replace(/\\/g, "/");
|
|
5782
|
+
if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized);
|
|
5783
|
+
const root = cwd === void 0 ? "" : cwd.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
5784
|
+
if (root !== "" && normalized === root) return sessionFileAddress(sessionId, "");
|
|
5785
|
+
if (root !== "" && normalized.startsWith(`${root}/`)) return sessionFileAddress(sessionId, normalized.slice(root.length + 1));
|
|
5786
|
+
return sessionFileAddress(sessionId, normalized);
|
|
5787
|
+
}
|
|
5788
|
+
//#endregion
|
|
5546
5789
|
//#region src/client/overview.ts
|
|
5547
5790
|
/**
|
|
5548
5791
|
* The Context Dashboard's data layer: everything the panel renders is derived
|
|
@@ -5576,21 +5819,6 @@ window.__ModuleLoader__.load({
|
|
|
5576
5819
|
return null;
|
|
5577
5820
|
}
|
|
5578
5821
|
}
|
|
5579
|
-
/** The running-session tally for the footer entry's badge (0 on any shape failure). */
|
|
5580
|
-
function runningCountOf(snapshot) {
|
|
5581
|
-
const state = asRecord(snapshot);
|
|
5582
|
-
if (state === null) return 0;
|
|
5583
|
-
const byId = asRecord(state.byId);
|
|
5584
|
-
if (byId === null) return 0;
|
|
5585
|
-
let count = 0;
|
|
5586
|
-
try {
|
|
5587
|
-
for (const id of Object.keys(byId)) {
|
|
5588
|
-
const row = asRecord(byId[id]);
|
|
5589
|
-
if (row !== null && row.running === true) count++;
|
|
5590
|
-
}
|
|
5591
|
-
} catch {}
|
|
5592
|
-
return count;
|
|
5593
|
-
}
|
|
5594
5822
|
/**
|
|
5595
5823
|
* The `useWorkspaces` standard prop read — same guarded-hook contract as
|
|
5596
5824
|
* {@link sessionsSnapshotOf}. The overview joins its session → workspace
|
|
@@ -5857,7 +6085,12 @@ window.__ModuleLoader__.load({
|
|
|
5857
6085
|
let toolsMs = 0;
|
|
5858
6086
|
let calls = 0;
|
|
5859
6087
|
let wallMs = 0;
|
|
6088
|
+
let costSessions = 0;
|
|
6089
|
+
let usageSessions = 0;
|
|
5860
6090
|
for (const row of rows) {
|
|
6091
|
+
const cost = row.timeline?.cost;
|
|
6092
|
+
if (estimateSessionCost(cost, prices, currency) !== null) costSessions++;
|
|
6093
|
+
if (usageTotalsOf(cost) !== null) usageSessions++;
|
|
5861
6094
|
turns += turnsOf(row.timeline);
|
|
5862
6095
|
const timing = row.timeline?.timing;
|
|
5863
6096
|
toolCalls += timing?.toolCalls ?? 0;
|
|
@@ -5871,7 +6104,9 @@ window.__ModuleLoader__.load({
|
|
|
5871
6104
|
tokens: totals?.total ?? 0,
|
|
5872
6105
|
turns,
|
|
5873
6106
|
cost: estimateSessionCost(usage, prices, currency),
|
|
6107
|
+
costSessions,
|
|
5874
6108
|
cacheHit: totals === null ? null : cacheHitPercent(totals.cacheRead, totals.input + totals.cacheRead + totals.cacheWrite),
|
|
6109
|
+
usageSessions,
|
|
5875
6110
|
toolCalls,
|
|
5876
6111
|
toolsMs,
|
|
5877
6112
|
calls,
|
|
@@ -5933,6 +6168,20 @@ window.__ModuleLoader__.load({
|
|
|
5933
6168
|
Promise.resolve(sessions.refresh()).catch(() => {});
|
|
5934
6169
|
} catch {}
|
|
5935
6170
|
}
|
|
6171
|
+
const BACKFILL_ROUTE = "/api/dsh-context/backfill";
|
|
6172
|
+
/**
|
|
6173
|
+
* Summon the host's projection warm-up (host/backfill.ts): the dashboard is
|
|
6174
|
+
* the rows' only reader, so the host defers the corpus-wide cold reads until
|
|
6175
|
+
* this surface first opens (one pass per host process — later opens no-op
|
|
6176
|
+
* server-side, and the host answers at once). Fire-and-forget: an older host
|
|
6177
|
+
* without the route, or a transport hiccup, just leaves the panel on the
|
|
6178
|
+
* rows it already has.
|
|
6179
|
+
*/
|
|
6180
|
+
function requestActivityBackfill() {
|
|
6181
|
+
try {
|
|
6182
|
+
fetch(BACKFILL_ROUTE, { method: "POST" }).catch(() => {});
|
|
6183
|
+
} catch {}
|
|
6184
|
+
}
|
|
5936
6185
|
/**
|
|
5937
6186
|
* The row's relative-activity label ("3m ago"), unit-stepped: under a
|
|
5938
6187
|
* minute reads "just now", then minutes, hours, days. A future or invalid
|
|
@@ -5948,258 +6197,6 @@ window.__ModuleLoader__.load({
|
|
|
5948
6197
|
return t("ov.time.d", { n: Math.floor(hours / 24) });
|
|
5949
6198
|
}
|
|
5950
6199
|
//#endregion
|
|
5951
|
-
//#region src/client/overviewStore.ts
|
|
5952
|
-
function createStore() {
|
|
5953
|
-
let open = false;
|
|
5954
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
5955
|
-
return {
|
|
5956
|
-
subscribe: (listener) => {
|
|
5957
|
-
listeners.add(listener);
|
|
5958
|
-
return () => {
|
|
5959
|
-
listeners.delete(listener);
|
|
5960
|
-
};
|
|
5961
|
-
},
|
|
5962
|
-
getSnapshot: () => open,
|
|
5963
|
-
set: (next) => {
|
|
5964
|
-
if (next === open) return;
|
|
5965
|
-
open = next;
|
|
5966
|
-
for (const listener of listeners) listener();
|
|
5967
|
-
}
|
|
5968
|
-
};
|
|
5969
|
-
}
|
|
5970
|
-
const overviewStore = createStore();
|
|
5971
|
-
//#endregion
|
|
5972
|
-
//#region src/client/components/overviewButton.tsx
|
|
5973
|
-
/**
|
|
5974
|
-
* The Context Dashboard's sidebar entry: a footer action stacked directly
|
|
5975
|
-
* above Settings (the harness's own foot layout: footer actions, then the
|
|
5976
|
-
* settings row), mirroring the Settings trigger's geometry in both column
|
|
5977
|
-
* widths. The button carries the plugin emblem and, on the wide
|
|
5978
|
-
* column, its label; a badge counts the sessions currently running (the one
|
|
5979
|
-
* glanceable live fact a footer can carry). Clicking opens the overview
|
|
5980
|
-
* overlay through the shared module store (overviewStore.ts).
|
|
5981
|
-
*
|
|
5982
|
-
* The per-user `insightsEntry` preference takes the entry down (renders
|
|
5983
|
-
* null) without unregistering the seat. The subscription fails open: an
|
|
5984
|
-
* absent settings store, an unserved namespace, or a value the plugin
|
|
5985
|
-
* cannot understand all leave the entry visible.
|
|
5986
|
-
*/
|
|
5987
|
-
/** Stable subscription faces for the settings-less degrade (no re-subscribes). */
|
|
5988
|
-
const subscribeNever = () => () => {};
|
|
5989
|
-
const entryShown = () => "show";
|
|
5990
|
-
function makeOverviewButton(kit, settings) {
|
|
5991
|
-
const { t } = kit;
|
|
5992
|
-
const subscribeEntry = settings === void 0 ? subscribeNever : (listener) => settings.store.subscribe(listener);
|
|
5993
|
-
const getEntry = settings === void 0 ? entryShown : () => settings.insightsEntry();
|
|
5994
|
-
return function OverviewButton(props) {
|
|
5995
|
-
const running = runningCountOf(sessionsSnapshotOf(props));
|
|
5996
|
-
if ((0, react.useSyncExternalStore)(subscribeEntry, getEntry) === "hide") return null;
|
|
5997
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5998
|
-
type: "button",
|
|
5999
|
-
className: props.wide === true ? "lc-ov-entry" : "lc-ov-entry lc-ov-entry-rail",
|
|
6000
|
-
title: t("ov.entry"),
|
|
6001
|
-
"aria-label": t("ov.entry"),
|
|
6002
|
-
onClick: () => {
|
|
6003
|
-
overviewStore.set(true);
|
|
6004
|
-
},
|
|
6005
|
-
children: [
|
|
6006
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextIcon, {
|
|
6007
|
-
size: props.wide === true ? 16 : 18,
|
|
6008
|
-
className: "lc-ov-entry-icon"
|
|
6009
|
-
}),
|
|
6010
|
-
props.wide === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6011
|
-
className: "lc-ov-entry-label",
|
|
6012
|
-
children: t("ov.entry")
|
|
6013
|
-
}),
|
|
6014
|
-
running > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6015
|
-
className: "lc-ov-badge",
|
|
6016
|
-
"aria-hidden": "true",
|
|
6017
|
-
children: running
|
|
6018
|
-
})
|
|
6019
|
-
]
|
|
6020
|
-
});
|
|
6021
|
-
};
|
|
6022
|
-
}
|
|
6023
|
-
//#endregion
|
|
6024
|
-
//#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/error.js
|
|
6025
|
-
/**
|
|
6026
|
-
* The only error thrown by the models.dev client.
|
|
6027
|
-
*
|
|
6028
|
-
* - `Transport` — the fetch itself failed (network, DNS, abort). `cause` is the underlying error.
|
|
6029
|
-
* - `UnexpectedStatus` — non-2xx response. `cause` is `{ status: number }`.
|
|
6030
|
-
* - `MalformedResponse` — the body was empty or not valid JSON. `cause` is the parse error, if any.
|
|
6031
|
-
*/
|
|
6032
|
-
var ModelsDevError = class extends Error {
|
|
6033
|
-
reason;
|
|
6034
|
-
name = "ModelsDevError";
|
|
6035
|
-
constructor(reason, options) {
|
|
6036
|
-
super(reason, options);
|
|
6037
|
-
this.reason = reason;
|
|
6038
|
-
}
|
|
6039
|
-
};
|
|
6040
|
-
//#endregion
|
|
6041
|
-
//#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/client.js
|
|
6042
|
-
/**
|
|
6043
|
-
* Creates a stateless models.dev client. Every method performs exactly one
|
|
6044
|
-
* `GET` and nothing is ever cached — callers who want caching should wrap
|
|
6045
|
-
* calls with their own policy. For a no-network alternative, see the
|
|
6046
|
-
* `@opencode-ai/models/snapshot` entrypoint.
|
|
6047
|
-
*/
|
|
6048
|
-
function make(options = {}) {
|
|
6049
|
-
const baseUrl = options.baseUrl ?? "https://models.dev";
|
|
6050
|
-
const base = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
|
|
6051
|
-
const request = async (path, requestOptions) => {
|
|
6052
|
-
const fetch = options.fetch ?? globalThis.fetch;
|
|
6053
|
-
const headers = new Headers();
|
|
6054
|
-
for (const [key, value] of new Headers(options.headers)) headers.set(key, value);
|
|
6055
|
-
for (const [key, value] of new Headers(requestOptions?.headers)) headers.set(key, value);
|
|
6056
|
-
let response;
|
|
6057
|
-
try {
|
|
6058
|
-
response = await fetch(new URL(path, base), {
|
|
6059
|
-
method: "GET",
|
|
6060
|
-
headers,
|
|
6061
|
-
signal: requestOptions?.signal
|
|
6062
|
-
});
|
|
6063
|
-
} catch (cause) {
|
|
6064
|
-
throw new ModelsDevError("Transport", { cause });
|
|
6065
|
-
}
|
|
6066
|
-
if (!response.ok) {
|
|
6067
|
-
try {
|
|
6068
|
-
await response.body?.cancel();
|
|
6069
|
-
} catch {}
|
|
6070
|
-
throw new ModelsDevError("UnexpectedStatus", { cause: { status: response.status } });
|
|
6071
|
-
}
|
|
6072
|
-
let text;
|
|
6073
|
-
try {
|
|
6074
|
-
text = await response.text();
|
|
6075
|
-
} catch (cause) {
|
|
6076
|
-
throw new ModelsDevError("Transport", { cause });
|
|
6077
|
-
}
|
|
6078
|
-
if (text === "") throw new ModelsDevError("MalformedResponse");
|
|
6079
|
-
try {
|
|
6080
|
-
return JSON.parse(text);
|
|
6081
|
-
} catch (cause) {
|
|
6082
|
-
throw new ModelsDevError("MalformedResponse", { cause });
|
|
6083
|
-
}
|
|
6084
|
-
};
|
|
6085
|
-
return {
|
|
6086
|
-
/** All providers with their models, pricing, and limits (`/api.json`). */
|
|
6087
|
-
providers: (requestOptions) => request("api.json", requestOptions),
|
|
6088
|
-
/** Provider-agnostic model metadata (`/models.json`). */
|
|
6089
|
-
models: (requestOptions) => request("models.json", requestOptions),
|
|
6090
|
-
/** Providers and model metadata in a single request (`/catalog.json`). */
|
|
6091
|
-
catalog: (requestOptions) => request("catalog.json", requestOptions)
|
|
6092
|
-
};
|
|
6093
|
-
}
|
|
6094
|
-
//#endregion
|
|
6095
|
-
//#region src/client/modelPrices.ts
|
|
6096
|
-
/**
|
|
6097
|
-
* The client's model-price book (client/cost.ts prices from it): the
|
|
6098
|
-
* models.dev registry, fetched through the official @opencode-ai/models
|
|
6099
|
-
* SDK. The registry payload is untrusted wire input, so `pricesBookOf`
|
|
6100
|
-
* re-proves every field at the boundary — a non-conforming provider/model/
|
|
6101
|
-
* cost entry drops whole. The book keeps EVERY provider that prices (keyed
|
|
6102
|
-
* by the registry's own provider id), so a dsh provider id outside the
|
|
6103
|
-
* rename table still prices by direct passthrough. One fetch per page load,
|
|
6104
|
-
* kicked on first subscribe; a failure degrades to a visible state the cost
|
|
6105
|
-
* cell notes, with a backed-off automatic retry — never a spinner, and
|
|
6106
|
-
* never an unhandled rejection.
|
|
6107
|
-
*/
|
|
6108
|
-
/** One finite non-negative registry figure, or null. */
|
|
6109
|
-
function rateOf(value) {
|
|
6110
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
6111
|
-
}
|
|
6112
|
-
/**
|
|
6113
|
-
* Extract the whole registry's per-model USD rates from a delivered
|
|
6114
|
-
* providers payload (`/api.json`), keyed by the registry's provider ids.
|
|
6115
|
-
* Any shape failure skips just that entry; a payload that is not a record
|
|
6116
|
-
* at all returns null (the store treats it as a failed fetch and retries).
|
|
6117
|
-
*/
|
|
6118
|
-
function pricesBookOf(value) {
|
|
6119
|
-
const data = asRecord(value);
|
|
6120
|
-
if (data === null || Array.isArray(data)) return null;
|
|
6121
|
-
const book = {};
|
|
6122
|
-
for (const providerId of Object.keys(data)) {
|
|
6123
|
-
const provider = asRecord(data[providerId]);
|
|
6124
|
-
const modelList = provider !== null ? asRecord(provider.models) : null;
|
|
6125
|
-
if (modelList === null) continue;
|
|
6126
|
-
const models = {};
|
|
6127
|
-
for (const id of Object.keys(modelList)) {
|
|
6128
|
-
const model = asRecord(modelList[id]);
|
|
6129
|
-
const cost = model !== null ? asRecord(model.cost) : null;
|
|
6130
|
-
if (cost === null) continue;
|
|
6131
|
-
const miss = rateOf(cost.input);
|
|
6132
|
-
const out = rateOf(cost.output);
|
|
6133
|
-
if (miss === null || out === null) continue;
|
|
6134
|
-
models[id] = {
|
|
6135
|
-
hit: rateOf(cost.cache_read) ?? miss,
|
|
6136
|
-
miss,
|
|
6137
|
-
write: rateOf(cost.cache_write) ?? miss,
|
|
6138
|
-
out
|
|
6139
|
-
};
|
|
6140
|
-
}
|
|
6141
|
-
if (Object.keys(models).length > 0) book[providerId] = models;
|
|
6142
|
-
}
|
|
6143
|
-
return book;
|
|
6144
|
-
}
|
|
6145
|
-
const defaultLoader = () => make().providers();
|
|
6146
|
-
/** The retry backoff base; each consecutive failure doubles the wait, capped at 3 doublings. */
|
|
6147
|
-
const RETRY_BASE_MS = 3e4;
|
|
6148
|
-
let loader = defaultLoader;
|
|
6149
|
-
let snap = {
|
|
6150
|
-
prices: null,
|
|
6151
|
-
failed: false
|
|
6152
|
-
};
|
|
6153
|
-
let inFlight = false;
|
|
6154
|
-
let failures = 0;
|
|
6155
|
-
let timer = null;
|
|
6156
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
6157
|
-
function fail() {
|
|
6158
|
-
snap = {
|
|
6159
|
-
...snap,
|
|
6160
|
-
failed: true
|
|
6161
|
-
};
|
|
6162
|
-
failures++;
|
|
6163
|
-
timer = setTimeout(() => {
|
|
6164
|
-
timer = null;
|
|
6165
|
-
fire();
|
|
6166
|
-
}, RETRY_BASE_MS * 2 ** Math.min(failures - 1, 3));
|
|
6167
|
-
}
|
|
6168
|
-
async function fire() {
|
|
6169
|
-
inFlight = true;
|
|
6170
|
-
try {
|
|
6171
|
-
const book = pricesBookOf(await loader());
|
|
6172
|
-
if (book !== null) {
|
|
6173
|
-
snap = {
|
|
6174
|
-
prices: book,
|
|
6175
|
-
failed: false
|
|
6176
|
-
};
|
|
6177
|
-
failures = 0;
|
|
6178
|
-
} else fail();
|
|
6179
|
-
} catch {
|
|
6180
|
-
fail();
|
|
6181
|
-
}
|
|
6182
|
-
inFlight = false;
|
|
6183
|
-
for (const fn of listeners) fn();
|
|
6184
|
-
}
|
|
6185
|
-
function kick() {
|
|
6186
|
-
if (snap.prices !== null || inFlight || timer !== null) return;
|
|
6187
|
-
fire();
|
|
6188
|
-
}
|
|
6189
|
-
/** The useSyncExternalStore seam: subscribing also kicks the first fetch. */
|
|
6190
|
-
const subscribeModelPrices = (fn) => {
|
|
6191
|
-
listeners.add(fn);
|
|
6192
|
-
kick();
|
|
6193
|
-
return () => {
|
|
6194
|
-
listeners.delete(fn);
|
|
6195
|
-
};
|
|
6196
|
-
};
|
|
6197
|
-
const getModelPricesSnap = () => snap;
|
|
6198
|
-
/** The stats board's read of the price book (null until the fetch lands). */
|
|
6199
|
-
function useModelPrices() {
|
|
6200
|
-
return (0, react.useSyncExternalStore)(subscribeModelPrices, getModelPricesSnap);
|
|
6201
|
-
}
|
|
6202
|
-
//#endregion
|
|
6203
6200
|
//#region src/shared/days.ts
|
|
6204
6201
|
/**
|
|
6205
6202
|
* The local-day key (`YYYY-MM-DD`) shared by the activity fold (host) and the
|
|
@@ -6685,7 +6682,10 @@ window.__ModuleLoader__.load({
|
|
|
6685
6682
|
const rows = (0, react.useMemo)(() => rowsOfSnapshot(snapshot, wsSnapshot), [snapshot, wsSnapshot]);
|
|
6686
6683
|
const groups = (0, react.useMemo)(() => sessionGroupsOf(wsSnapshot), [wsSnapshot]);
|
|
6687
6684
|
(0, react.useEffect)(() => {
|
|
6688
|
-
if (open)
|
|
6685
|
+
if (open) {
|
|
6686
|
+
requestActivityBackfill();
|
|
6687
|
+
refreshSessions(ctx);
|
|
6688
|
+
}
|
|
6689
6689
|
}, [open]);
|
|
6690
6690
|
(0, react.useEffect)(() => {
|
|
6691
6691
|
setPage(0);
|
|
@@ -6824,7 +6824,7 @@ window.__ModuleLoader__.load({
|
|
|
6824
6824
|
}),
|
|
6825
6825
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6826
6826
|
className: "lc-stat-sub",
|
|
6827
|
-
children: t("ov.kpi.sessionsSub", { n: kpi.
|
|
6827
|
+
children: t("ov.kpi.sessionsSub", { n: kpi.costSessions })
|
|
6828
6828
|
})
|
|
6829
6829
|
]
|
|
6830
6830
|
}),
|
|
@@ -6841,7 +6841,7 @@ window.__ModuleLoader__.load({
|
|
|
6841
6841
|
}),
|
|
6842
6842
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6843
6843
|
className: "lc-stat-sub",
|
|
6844
|
-
children: t("ov.kpi.sessionsSub", { n: kpi.
|
|
6844
|
+
children: t("ov.kpi.sessionsSub", { n: kpi.usageSessions })
|
|
6845
6845
|
})
|
|
6846
6846
|
]
|
|
6847
6847
|
}),
|
|
@@ -9492,7 +9492,7 @@ window.__ModuleLoader__.load({
|
|
|
9492
9492
|
return function PluginInfo() {
|
|
9493
9493
|
const [latest, setLatest] = (0, react.useState)(null);
|
|
9494
9494
|
(0, react.useEffect)(() => {
|
|
9495
|
-
if ("0.53.
|
|
9495
|
+
if ("0.53.3".includes("-dev")) return;
|
|
9496
9496
|
let on = true;
|
|
9497
9497
|
fetchLatestVersion().then((v) => {
|
|
9498
9498
|
if (on && v) setLatest(v);
|
|
@@ -9501,8 +9501,8 @@ window.__ModuleLoader__.load({
|
|
|
9501
9501
|
on = false;
|
|
9502
9502
|
};
|
|
9503
9503
|
}, []);
|
|
9504
|
-
const update = latest !== null && isNewerVersion(latest, "0.53.
|
|
9505
|
-
const nameText = "dsh-context (v0.53.
|
|
9504
|
+
const update = latest !== null && isNewerVersion(latest, "0.53.3") ? latest : null;
|
|
9505
|
+
const nameText = "dsh-context (v0.53.3)";
|
|
9506
9506
|
const nameValue = [nameText];
|
|
9507
9507
|
if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9508
9508
|
className: "lc-pi-update",
|
|
@@ -11348,7 +11348,7 @@ window.__ModuleLoader__.load({
|
|
|
11348
11348
|
}
|
|
11349
11349
|
//#endregion
|
|
11350
11350
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/overview.css.mjs
|
|
11351
|
-
const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;position:relative;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-badge{box-sizing:border-box;background:var(--color-green-500);color:#fff;pointer-events:none;border-radius:999px;justify-content:center;align-items:center;min-width:15px;height:15px;padding:0 4px;font-size:9px;font-weight:700;line-height:1;display:flex;position:absolute;top:1px;right:4px}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);max-height:min(86vh,900px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:13px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:13px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
|
|
11351
|
+
const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);max-height:min(86vh,900px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:13px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:13px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
|
|
11352
11352
|
const tagId = "dsh-context/overview.css";
|
|
11353
11353
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
11354
11354
|
const tag = document.createElement("style");
|
package/lib/index.js
CHANGED
|
@@ -870,7 +870,7 @@ function removeSurfaceSeqs(st, claimed, goneSeq) {
|
|
|
870
870
|
* rather than through `deriveEventMessage`, whose 0.1.2-rc.1 generation knows
|
|
871
871
|
* nothing of the V3 `system/message` variant. A malformed payload reads null.
|
|
872
872
|
*/
|
|
873
|
-
function messageOf(data) {
|
|
873
|
+
function messageOf$1(data) {
|
|
874
874
|
const message = data?.message;
|
|
875
875
|
return message !== null && typeof message === "object" ? message : null;
|
|
876
876
|
}
|
|
@@ -1230,7 +1230,7 @@ function applyTimeline(state, event, bounds) {
|
|
|
1230
1230
|
pushSystem(s, {
|
|
1231
1231
|
seq: event.seq,
|
|
1232
1232
|
time: event.time,
|
|
1233
|
-
tokens: estimateSystemContent(messageOf(data)?.content)
|
|
1233
|
+
tokens: estimateSystemContent(messageOf$1(data)?.content)
|
|
1234
1234
|
});
|
|
1235
1235
|
break;
|
|
1236
1236
|
}
|
|
@@ -2058,8 +2058,17 @@ function createToolAttribution(ctx) {
|
|
|
2058
2058
|
}
|
|
2059
2059
|
//#endregion
|
|
2060
2060
|
//#region src/host/backfill.ts
|
|
2061
|
-
/**
|
|
2062
|
-
const
|
|
2061
|
+
/** The plugin's warm-up trigger route, under the authenticated `/api` fence. */
|
|
2062
|
+
const BACKFILL_ROUTE = "/api/dsh-context/backfill";
|
|
2063
|
+
/**
|
|
2064
|
+
* Inter-session pacing so a multi-hundred-session backfill never starve the
|
|
2065
|
+
* host: each cold read is a full durable-log decode, and a boot-time host
|
|
2066
|
+
* under sustained read pressure stalls its own client handshake (the
|
|
2067
|
+
* workspace sidebar rides it), so the pass breathes between sessions.
|
|
2068
|
+
*/
|
|
2069
|
+
const YIELD_MS = 100;
|
|
2070
|
+
/** The `name` dsh raises for a durable log its migration surface refuses. */
|
|
2071
|
+
const UNSUPPORTED_MIGRATION = "SessionFormatUnsupportedMigrationError";
|
|
2063
2072
|
function asRecord(value) {
|
|
2064
2073
|
return value !== null && typeof value === "object" ? value : null;
|
|
2065
2074
|
}
|
|
@@ -2114,11 +2123,63 @@ function isLive(sessions, id) {
|
|
|
2114
2123
|
}
|
|
2115
2124
|
}
|
|
2116
2125
|
/**
|
|
2117
|
-
*
|
|
2118
|
-
*
|
|
2126
|
+
* Whether the cold read failed because the running harness refuses to
|
|
2127
|
+
* migrate this log — a permanent, source-side refusal (the artifact is left
|
|
2128
|
+
* unchanged, so retrying cannot succeed). Matched by the error's documented
|
|
2129
|
+
* `name`, so no dsh symbol needs importing and every supported baseline
|
|
2130
|
+
* classifies alike: an unknown error shape just falls to the warn path.
|
|
2131
|
+
*/
|
|
2132
|
+
function isUnsupportedMigration(error) {
|
|
2133
|
+
try {
|
|
2134
|
+
return asRecord(error)?.name === UNSUPPORTED_MIGRATION;
|
|
2135
|
+
} catch {
|
|
2136
|
+
return false;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
/** The error's printable form — a hostile error may throw on its own toString. */
|
|
2140
|
+
function messageOf(error) {
|
|
2141
|
+
try {
|
|
2142
|
+
return String(error);
|
|
2143
|
+
} catch {
|
|
2144
|
+
return "unprintable error";
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
/**
|
|
2148
|
+
* Arm the warm-up behind its trigger route. The route flips `requested`
|
|
2149
|
+
* when the dashboard first opens; the cold-path faces compose `launch`.
|
|
2150
|
+
* Either half may land first; the first request after both launches the one
|
|
2151
|
+
* pass, and every later request is a no-op. Returns the deferred injects'
|
|
2152
|
+
* disposer (abort on unload).
|
|
2119
2153
|
*/
|
|
2120
2154
|
function watchActivityBackfill(ctx) {
|
|
2121
|
-
|
|
2155
|
+
let requested = false;
|
|
2156
|
+
let started = false;
|
|
2157
|
+
let launch = null;
|
|
2158
|
+
const tryLaunch = () => {
|
|
2159
|
+
if (!requested || started || launch === null) return;
|
|
2160
|
+
started = true;
|
|
2161
|
+
launch();
|
|
2162
|
+
};
|
|
2163
|
+
const routeFiber = ctx.inject(["connection"], (c) => {
|
|
2164
|
+
const connection = c.get("connection");
|
|
2165
|
+
const register = typeof connection?.fetch?.register === "function" ? connection.fetch.register.bind(connection.fetch) : void 0;
|
|
2166
|
+
if (register === void 0) return;
|
|
2167
|
+
try {
|
|
2168
|
+
c.effect(() => register({
|
|
2169
|
+
path: BACKFILL_ROUTE,
|
|
2170
|
+
methods: ["POST"],
|
|
2171
|
+
requestBody: "buffered",
|
|
2172
|
+
fetch: () => {
|
|
2173
|
+
requested = true;
|
|
2174
|
+
tryLaunch();
|
|
2175
|
+
return Response.json({ ok: true }, { headers: { "cache-control": "no-store" } });
|
|
2176
|
+
}
|
|
2177
|
+
}), "dsh-context: backfill route");
|
|
2178
|
+
} catch {
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
});
|
|
2182
|
+
const runnerFiber = ctx.inject([
|
|
2122
2183
|
"sessionQuery",
|
|
2123
2184
|
"sessionProjectionCache",
|
|
2124
2185
|
"sessionPersistence",
|
|
@@ -2137,6 +2198,7 @@ function watchActivityBackfill(ctx) {
|
|
|
2137
2198
|
const listed = await query.listSessions(abort.signal);
|
|
2138
2199
|
if (!Array.isArray(listed)) return;
|
|
2139
2200
|
let folded = 0;
|
|
2201
|
+
let refused = 0;
|
|
2140
2202
|
for (const record of listed) {
|
|
2141
2203
|
if (abort.signal.aborted) return;
|
|
2142
2204
|
const header = headerOf(record);
|
|
@@ -2148,21 +2210,28 @@ function watchActivityBackfill(ctx) {
|
|
|
2148
2210
|
cache.coldSnapshot(log.header, log.inheritedEventCount, log.events);
|
|
2149
2211
|
folded++;
|
|
2150
2212
|
} catch (error) {
|
|
2151
|
-
|
|
2213
|
+
if (isUnsupportedMigration(error)) {
|
|
2214
|
+
refused++;
|
|
2215
|
+
ctx.logger.debug(`dsh-context: projection backfill skipped "${header.id}" (${messageOf(error)})`);
|
|
2216
|
+
} else ctx.logger.warn(`dsh-context: projection backfill skipped "${header.id}" (${messageOf(error)})`);
|
|
2152
2217
|
}
|
|
2153
2218
|
await new Promise((resolve) => setTimeout(resolve, YIELD_MS));
|
|
2154
2219
|
}
|
|
2155
2220
|
if (folded > 0) ctx.logger.info(`dsh-context: projection rows backfilled for ${folded} session(s)`);
|
|
2221
|
+
if (refused > 0) ctx.logger.info(`dsh-context: projection backfill skipped ${refused} session(s) (legacy log format is not migratable; sources left unchanged)`);
|
|
2156
2222
|
};
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2223
|
+
launch = () => {
|
|
2224
|
+
run().catch((error) => {
|
|
2225
|
+
if (!abort.signal.aborted) ctx.logger.warn(`dsh-context: projection backfill stopped early (${messageOf(error)})`);
|
|
2226
|
+
});
|
|
2227
|
+
};
|
|
2228
|
+
tryLaunch();
|
|
2160
2229
|
return () => {
|
|
2161
2230
|
abort.abort();
|
|
2162
2231
|
};
|
|
2163
2232
|
});
|
|
2164
2233
|
return () => {
|
|
2165
|
-
|
|
2234
|
+
for (const fiber of [routeFiber, runnerFiber]) fiber?.dispose?.();
|
|
2166
2235
|
};
|
|
2167
2236
|
}
|
|
2168
2237
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.53.
|
|
3
|
+
"version": "0.53.3",
|
|
4
4
|
"description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
|
|
5
5
|
"author": "bowenliang123",
|
|
6
6
|
"repository": {
|