dsh-context 0.50.0 → 0.51.0

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/index.d.ts CHANGED
@@ -128,11 +128,12 @@ interface TimelineState {
128
128
  */
129
129
  archived: SurfaceNode[];
130
130
  /**
131
- * Session-cost raw material: cumulative billed-token totals per DeepSeek
132
- * model family and pricing period (see SessionCostUsage). Running
133
- * totals — never trimmed, so the estimate always covers the COMPLETE
134
- * session log even after the request/event retention bounds cut in.
135
- * Absent until a DeepSeek flash/pro request reports usage.
131
+ * Session-cost raw material: cumulative billed-token totals per
132
+ * (provider, model), split into pricing periods for DeepSeek (see
133
+ * SessionCostUsage / CostModelUsage). Running totals — never trimmed, so
134
+ * the estimate always covers the COMPLETE session log even after the
135
+ * request/event retention bounds cut in. Absent until a usage-reporting
136
+ * request with a known model folds.
136
137
  */
137
138
  cost?: SessionCostUsage;
138
139
  /**
@@ -374,9 +375,9 @@ interface Snapshot {
374
375
  requests: RequestRecord[];
375
376
  events: ContextEventRecord[];
376
377
  /**
377
- * Cumulative session-cost raw material (per-family, per-period billed
378
- * token totals — see SessionCostUsage). Absent until a DeepSeek V4
379
- * request reports usage.
378
+ * Cumulative session-cost raw material (per-provider, per-model billed
379
+ * token totals — see SessionCostUsage). Absent until a request with a
380
+ * known model reports usage.
380
381
  */
381
382
  cost?: SessionCostUsage;
382
383
  /**
@@ -573,22 +574,28 @@ interface TimingTotals {
573
574
  /** Per-tool-name tallies behind the timing card's ranking (bounded). */
574
575
  tools: Record<string, ToolTimingTotals>;
575
576
  }
576
- /** One model family's totals split by DeepSeek's pricing period (Beijing Time). */
577
- interface CostFamilyUsage {
577
+ /**
578
+ * One billed model's cumulative totals split by pricing period. Providers
579
+ * without period-based pricing book everything under `peak` (the list-price
580
+ * period); DeepSeek splits at fold time — peak windows bill at list price,
581
+ * off-peak (all other hours) at half.
582
+ */
583
+ interface CostModelUsage {
578
584
  peak?: CostBucketTotals;
579
585
  off?: CostBucketTotals;
580
586
  }
581
587
  /**
582
588
  * The session-cost estimate's raw material: cumulative provider-reported
583
- * token totals per DeepSeek model family (matched on the model NAME,
584
- * provider-agnostic) and pricing period. The Client prices these with its
585
- * hardcoded list-price table in the locale's currency. Absent until a
586
- * deepseek-flash / deepseek-v4-flash / deepseek-v4-pro request reports
587
- * usage.
589
+ * billed-token totals, keyed by the request envelope's DSH provider id (''
590
+ * when a log carries none) and then by its model id — the exact (provider,
591
+ * model) faces the Client's model-price book resolves (the models.dev
592
+ * registry, client/modelPrices.ts). Running totals per key; absent until a
593
+ * request with a known model reports usage.
588
594
  */
589
595
  interface SessionCostUsage {
590
- flash?: CostFamilyUsage;
591
- pro?: CostFamilyUsage;
596
+ [provider: string]: {
597
+ [model: string]: CostModelUsage;
598
+ };
592
599
  }
593
600
  /** One model-visible message on the surface, with its heuristic token price. */
594
601
  interface SurfaceNode {
package/lib/index.js CHANGED
@@ -293,6 +293,29 @@ function resolveBounds(config) {
293
293
  return Config.parse(config ?? {});
294
294
  }
295
295
  //#endregion
296
+ //#region src/shared/providers.ts
297
+ /**
298
+ * The provider-id seam between dsh request envelopes and the models.dev
299
+ * registry: client/cost.ts resolves price-book branches through it, and
300
+ * host/fold.ts uses the DeepSeek resolution to split the session-cost totals
301
+ * into peak/off-peak periods. Only the renames live here — an id absent
302
+ * from the table passes through verbatim.
303
+ */
304
+ const MODELS_DEV_PROVIDER_IDS = {
305
+ "deepseek-official": "deepseek",
306
+ "kimi-coding": "moonshotai",
307
+ "minimax-cn": "minimax",
308
+ "zai-coding-cn": "zhipuai"
309
+ };
310
+ /** The models.dev provider id that prices a dsh provider (identity for unmapped ids). */
311
+ function modelsDevProviderOf(dshProviderId) {
312
+ return MODELS_DEV_PROVIDER_IDS[dshProviderId] ?? dshProviderId;
313
+ }
314
+ /** Whether a dsh provider prices through DeepSeek's period-based list (peak / half-price off-peak). */
315
+ function isDeepSeekProvider(dshProviderId) {
316
+ return modelsDevProviderOf(dshProviderId) === "deepseek";
317
+ }
318
+ //#endregion
296
319
  //#region src/shared/estimate.ts
297
320
  /**
298
321
  * Token heuristics shared by the host fold and the client boundary — the
@@ -1274,31 +1297,16 @@ function tokenCountOf(value) {
1274
1297
  return null;
1275
1298
  }
1276
1299
  /**
1277
- * The DeepSeek model family a model name prices as — matched on the NAME
1278
- * alone (provider-agnostic: official API, proxies, OpenRouter spellings like
1279
- * `deepseek/deepseek-v4.1-flash` and `deepseek/deepseek-flash` all land
1280
- * here). The name must carry a DeepSeek marker (`v4` or `deepseek`) so a
1281
- * foreign flash/pro-named model (gemini-2.0-flash) is never priced.
1282
- */
1283
- function costFamilyOf(model) {
1284
- if (model === void 0) return null;
1285
- const m = model.toLowerCase();
1286
- if (!m.includes("v4") && !m.includes("deepseek")) return null;
1287
- if (m.includes("flash")) return "flash";
1288
- if (m.includes("pro")) return "pro";
1289
- return null;
1290
- }
1291
- /**
1292
- * DeepSeek's peak windows (Beijing Time, UTC+8): 09:00-12:00 and 14:00-18:00
1293
- * on weekdays; off-peak (half the peak rate) covers all other hours plus all
1294
- * of Saturday and Sunday.
1300
+ * DeepSeek's peak windows (the official list: UTC 01:00–04:00 and 06:00–10:00,
1301
+ * Monday through Friday — Beijing Time 09:00–12:00 and 14:00–18:00). All other
1302
+ * hours, plus entire weekends, bill at the half-price off-peak rate.
1295
1303
  */
1296
1304
  function isPeakUtc(time) {
1297
- const bj = new Date(time + 288e5);
1298
- const day = bj.getUTCDay();
1305
+ const at = new Date(time);
1306
+ const day = at.getUTCDay();
1299
1307
  if (day === 0 || day === 6) return false;
1300
- const h = bj.getUTCHours();
1301
- return h >= 9 && h < 12 || h >= 14 && h < 18;
1308
+ const h = at.getUTCHours();
1309
+ return h >= 1 && h < 4 || h >= 6 && h < 10;
1302
1310
  }
1303
1311
  /**
1304
1312
  * Fold one billed request into the session-cost totals, cloning along the
@@ -1306,29 +1314,42 @@ function isPeakUtc(time) {
1306
1314
  * previous state — the apply contract never mutates it in place). The
1307
1315
  * buckets arrive sanitized ({@link BilledUsage}), so the totals stay at the
1308
1316
  * schemas' non-negative safe integers no matter what the provider reported.
1317
+ * The key is the request envelope's (provider, model) face — the exact
1318
+ * lookup the Client's model-price book resolves (models.dev). A request
1319
+ * without a provider still accumulates (under the '' key) and the Client
1320
+ * prices it when the model id is unambiguous; without a model there is
1321
+ * nothing to price. DeepSeek's period-based list splits the buckets
1322
+ * (peak windows at list price, all other hours half price); every other
1323
+ * provider books everything under the list-price period.
1309
1324
  */
1310
1325
  function accumulateCost(st, time, usage) {
1311
- const family = costFamilyOf(st.model);
1312
- if (family === null) return;
1313
- const prev = st.cost ?? {};
1314
- const fam = prev[family] ?? {};
1315
- const period = isPeakUtc(time) ? "peak" : "off";
1316
- const b = fam[period] ?? {
1326
+ const model = st.model;
1327
+ if (model === void 0) return;
1328
+ const provider = st.provider ?? "";
1329
+ const period = isDeepSeekProvider(provider) && !isPeakUtc(time) ? "off" : "peak";
1330
+ const models = st.cost?.[provider] ?? {};
1331
+ const periods = models[model] ?? {};
1332
+ const b = periods[period] ?? {
1317
1333
  uncached: 0,
1318
1334
  cacheRead: 0,
1319
1335
  cacheWrite: 0,
1320
1336
  output: 0
1321
1337
  };
1322
- const nextFam = { ...fam };
1323
- nextFam[period] = {
1338
+ const nextPeriods = { ...periods };
1339
+ nextPeriods[period] = {
1324
1340
  uncached: b.uncached + usage.input,
1325
1341
  cacheRead: b.cacheRead + usage.cacheRead,
1326
1342
  cacheWrite: b.cacheWrite + usage.cacheWrite,
1327
1343
  output: b.output + usage.output
1328
1344
  };
1329
- const next = { ...prev };
1330
- next[family] = nextFam;
1331
- st.cost = next;
1345
+ const nextModels = {
1346
+ ...models,
1347
+ [model]: nextPeriods
1348
+ };
1349
+ st.cost = {
1350
+ ...st.cost ?? {},
1351
+ [provider]: nextModels
1352
+ };
1332
1353
  }
1333
1354
  /**
1334
1355
  * Advance the fold over ONE committed session event under the projection
@@ -1791,18 +1812,18 @@ function headFieldsOf(state) {
1791
1812
  archive: []
1792
1813
  };
1793
1814
  if (state.cost !== void 0) {
1794
- const copyFam = (f) => {
1795
- if (f === void 0) return void 0;
1796
- const out = {};
1797
- if (f.peak !== void 0) out.peak = { ...f.peak };
1798
- if (f.off !== void 0) out.off = { ...f.off };
1799
- return out;
1800
- };
1801
1815
  const cost = {};
1802
- const flash = copyFam(state.cost.flash);
1803
- if (flash !== void 0) cost.flash = flash;
1804
- const pro = copyFam(state.cost.pro);
1805
- if (pro !== void 0) cost.pro = pro;
1816
+ for (const provider in state.cost) {
1817
+ const models = {};
1818
+ for (const model in state.cost[provider]) {
1819
+ const periods = state.cost[provider][model];
1820
+ const copy = {};
1821
+ if (periods.peak !== void 0) copy.peak = { ...periods.peak };
1822
+ if (periods.off !== void 0) copy.off = { ...periods.off };
1823
+ models[model] = copy;
1824
+ }
1825
+ cost[provider] = models;
1826
+ }
1806
1827
  result.cost = cost;
1807
1828
  }
1808
1829
  if (state.timing !== void 0) {
@@ -2340,10 +2361,12 @@ const costBucketsSchema = z.object({
2340
2361
  cacheWrite: z.number().int().nonnegative(),
2341
2362
  output: z.number().int().nonnegative()
2342
2363
  }).strict();
2343
- const costFamilySchema = z.object({
2364
+ const costModelSchema = z.object({
2344
2365
  peak: costBucketsSchema.optional(),
2345
2366
  off: costBucketsSchema.optional()
2346
2367
  }).strict();
2368
+ const costModelsSchema = z.record(z.string(), costModelSchema);
2369
+ const costUsageSchema = z.record(z.string(), costModelsSchema);
2347
2370
  const toolTimingSchema = z.object({
2348
2371
  calls: z.number().int().nonnegative(),
2349
2372
  ms: z.number().nonnegative()
@@ -2402,10 +2425,7 @@ const contextTimelineSchema = z.object({
2402
2425
  detailRev: z.number().int().nonnegative().optional(),
2403
2426
  requests: z.array(requestRecordSchema).optional(),
2404
2427
  events: z.array(contextEventSchema).optional(),
2405
- cost: z.object({
2406
- flash: costFamilySchema.optional(),
2407
- pro: costFamilySchema.optional()
2408
- }).strict().optional(),
2428
+ cost: costUsageSchema.optional(),
2409
2429
  timing: timingTotalsSchema.optional(),
2410
2430
  systems: z.array(systemPromptNodeSchema).optional(),
2411
2431
  nodes: z.array(surfaceNodeSchema).optional(),
@@ -2441,10 +2461,7 @@ const timelineStateSchema = z.object({
2441
2461
  requests: z.array(requestRecordSchema),
2442
2462
  events: z.array(contextEventSchema),
2443
2463
  archived: z.array(surfaceNodeSchema),
2444
- cost: z.object({
2445
- flash: costFamilySchema.optional(),
2446
- pro: costFamilySchema.optional()
2447
- }).strict().optional(),
2464
+ cost: costUsageSchema.optional(),
2448
2465
  archiveFloor: z.number().optional(),
2449
2466
  timing: timingTotalsSchema.optional(),
2450
2467
  humanInputs: z.number().int().nonnegative().optional(),
@@ -2522,7 +2539,7 @@ function createContextTimelineDefinition(config, slim) {
2522
2539
  },
2523
2540
  init: () => createTimelineState(),
2524
2541
  apply: (state, event) => applyTimeline(state, event, bounds),
2525
- stateVersion: 16
2542
+ stateVersion: 18
2526
2543
  };
2527
2544
  }
2528
2545
  //#endregion
@@ -2625,6 +2642,11 @@ const SettingsSchema = z$1.object({
2625
2642
  ]).default("all").loose(),
2626
2643
  defaultGranularity: z$1.union(["step", "turn"]).default("step"),
2627
2644
  defaultTrendMode: z$1.union(["total", "delta"]).default("total").loose(),
2645
+ defaultToolSort: z$1.union([
2646
+ "size",
2647
+ "count",
2648
+ "name"
2649
+ ]).default("count").loose(),
2628
2650
  defaultFileSort: z$1.union([
2629
2651
  "count",
2630
2652
  "latest",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.50.0",
3
+ "version": "0.51.0",
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": {
@@ -109,7 +109,10 @@
109
109
  "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
110
110
  "@deepseek-ai/dsh-util-workspace-path": "0.1.5-rc.1",
111
111
  "@deepseek-ai/schemastery": "^3.18.2",
112
+ "@opencode-ai/models": "^0.0.72",
112
113
  "@stylistic/eslint-plugin": "^5.10.0",
114
+ "@tailwindcss/node": "4.3.3",
115
+ "@tailwindcss/oxide": "4.3.3",
113
116
  "@types/node": "^26.2.0",
114
117
  "@types/react": "^18.3.31",
115
118
  "@types/react-dom": "^18.3.7",
@@ -117,12 +120,13 @@
117
120
  "eslint-plugin-sonarjs": "^4.2.0",
118
121
  "husky": "^9.1.7",
119
122
  "jsdom": "^30.0.1",
120
- "lightningcss": "^1.33.0",
123
+ "lightningcss": "1.32.0",
121
124
  "oxlint": "1.82.0",
122
125
  "oxlint-tsgolint": "7.0.2001",
123
126
  "playwright-core": "^1.62.1",
124
127
  "react": "^18.3.1",
125
128
  "react-dom": "^18.3.1",
129
+ "tailwindcss": "4.3.3",
126
130
  "tsdown": "^0.23.0",
127
131
  "typescript": "^7.0.2",
128
132
  "vitest": "^4.1.11",