dsh-context 0.58.0 → 0.59.1
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 +2 -1
- package/lib/client.js +663 -426
- package/lib/index.d.ts +16 -0
- package/lib/index.js +152 -50
- package/package.json +14 -13
package/lib/index.d.ts
CHANGED
|
@@ -8,7 +8,13 @@ interface ActivityState {
|
|
|
8
8
|
days: Record<string, {
|
|
9
9
|
tokens: number;
|
|
10
10
|
requests: number;
|
|
11
|
+
cost?: SessionCostUsage;
|
|
11
12
|
}>;
|
|
13
|
+
/** The route in force (last `request/header`'s config wins) — prices each settlement. */
|
|
14
|
+
model?: string;
|
|
15
|
+
provider?: string;
|
|
16
|
+
/** The open step's start instant (armed by `step/start`, cleared by `step/end`). */
|
|
17
|
+
stepStart?: number;
|
|
12
18
|
}
|
|
13
19
|
//#endregion
|
|
14
20
|
//#region src/host/headers.d.ts
|
|
@@ -299,6 +305,16 @@ interface ActivityDay {
|
|
|
299
305
|
tokens: number;
|
|
300
306
|
/** Completed model calls (assistant settlements) that day. */
|
|
301
307
|
requests: number;
|
|
308
|
+
/**
|
|
309
|
+
* The day's billed buckets keyed provider → model → pricing period — the
|
|
310
|
+
* same SessionCostUsage raw material the timeline's session-cost totals
|
|
311
|
+
* carry, scoped to the day the requests INITIATED in, so the client prices
|
|
312
|
+
* each day off the same model-price book as the KPI band. ADDITIVE-OPTIONAL:
|
|
313
|
+
* absent on rows folded before the field existed (older plugin builds), on
|
|
314
|
+
* days whose settlements predate any model header, and on settlements the
|
|
315
|
+
* provider left unmetered — the usage chart degrades to tokens-only bars.
|
|
316
|
+
*/
|
|
317
|
+
cost?: SessionCostUsage;
|
|
302
318
|
}
|
|
303
319
|
/**
|
|
304
320
|
* The per-session daily activity ledger (`contextActivity` wire value):
|
package/lib/index.js
CHANGED
|
@@ -1056,6 +1056,10 @@ function tokenCountOf(value) {
|
|
|
1056
1056
|
* DeepSeek's peak windows (the official list: UTC 01:00–04:00 and 06:00–10:00,
|
|
1057
1057
|
* Monday through Friday — Beijing Time 09:00–12:00 and 14:00–18:00). All other
|
|
1058
1058
|
* hours, plus entire weekends, bill at the half-price off-peak rate.
|
|
1059
|
+
*
|
|
1060
|
+
* Exported for the activity unit (host/activity.ts): the daily ledger prices
|
|
1061
|
+
* each day's buckets with the same split, so its per-day fees sum to the
|
|
1062
|
+
* session-cost totals the timeline fold books.
|
|
1059
1063
|
*/
|
|
1060
1064
|
function isPeakUtc(time) {
|
|
1061
1065
|
const at = new Date(time);
|
|
@@ -1744,7 +1748,8 @@ function dayKeyOf(time) {
|
|
|
1744
1748
|
//#region src/host/activity.ts
|
|
1745
1749
|
/**
|
|
1746
1750
|
* The `contextActivity` session projection unit — the per-day activity
|
|
1747
|
-
* ledger behind the Context Dashboard's heatmap
|
|
1751
|
+
* ledger behind the Context Dashboard's heatmap and its last-7-days usage
|
|
1752
|
+
* chart.
|
|
1748
1753
|
*
|
|
1749
1754
|
* The timeline unit (fold.ts) is a "current snapshot" fold: it prices the
|
|
1750
1755
|
* context as it stands NOW, which cannot draw a per-day chart. This unit
|
|
@@ -1753,75 +1758,165 @@ function dayKeyOf(time) {
|
|
|
1753
1758
|
* completed request to its day and, when the provider reported a usage
|
|
1754
1759
|
* object, the day's billed-token figure grows by the disjoint buckets
|
|
1755
1760
|
* (uncached input + cache read + cache write + output — the same semantics
|
|
1756
|
-
* the timeline's request records and the official token meter use).
|
|
1757
|
-
*
|
|
1758
|
-
*
|
|
1761
|
+
* the timeline's request records and the official token meter use). Each
|
|
1762
|
+
* metered settlement also books its buckets into the day's per-(provider,
|
|
1763
|
+
* model, period) pricing record — the same raw material the timeline's
|
|
1764
|
+
* session-cost totals carry (SessionCostUsage), so the client prices every
|
|
1765
|
+
* day off the SAME model-price book and estimator it prices the KPI band
|
|
1766
|
+
* with. Buckets attribute to the day the open STEP STARTED (the turn's
|
|
1767
|
+
* initiation, armed by `step/start` and cleared by `step/end` — the
|
|
1768
|
+
* timeline fold's own pending-slot protocol), falling back to the
|
|
1769
|
+
* settlement instant when no stamp is armed; the peak/off split prices the
|
|
1770
|
+
* same instant, so a fee always lands on one day at one rate.
|
|
1759
1771
|
*
|
|
1760
|
-
* One wire contract, one small state: a day entry is two integers
|
|
1761
|
-
*
|
|
1762
|
-
*
|
|
1763
|
-
*
|
|
1764
|
-
*
|
|
1765
|
-
*
|
|
1766
|
-
* state
|
|
1772
|
+
* One wire contract, one small state: a day entry is two integers plus the
|
|
1773
|
+
* optional pricing record, the route in force rides the state (last
|
|
1774
|
+
* `request/header` wins), and the retention cap keeps at most
|
|
1775
|
+
* {@link MAX_KEPT_DAYS} keys, so the value riding every session-list row
|
|
1776
|
+
* stays trivial next to the timeline head. Same projection contract as the
|
|
1777
|
+
* sibling units: pure init/apply/view, unknown or malformed events return
|
|
1778
|
+
* the state unchanged, and no `undefined`-valued property ever materializes
|
|
1779
|
+
* (the plain-JSON persisted-state precondition).
|
|
1767
1780
|
*/
|
|
1768
1781
|
/**
|
|
1769
1782
|
* Retention cap on ledger days. A little over a year of daily-active
|
|
1770
1783
|
* sessions; the oldest keys drop first (key order IS chronological order).
|
|
1771
1784
|
*/
|
|
1772
1785
|
const MAX_KEPT_DAYS = 400;
|
|
1786
|
+
const costBucketSchema = z.object({
|
|
1787
|
+
uncached: z.number().int().nonnegative(),
|
|
1788
|
+
cacheRead: z.number().int().nonnegative(),
|
|
1789
|
+
cacheWrite: z.number().int().nonnegative(),
|
|
1790
|
+
output: z.number().int().nonnegative()
|
|
1791
|
+
}).strict();
|
|
1792
|
+
const costModelSchema$1 = z.object({
|
|
1793
|
+
peak: costBucketSchema.optional(),
|
|
1794
|
+
off: costBucketSchema.optional()
|
|
1795
|
+
}).strict();
|
|
1796
|
+
const costUsageSchema$1 = z.record(z.string(), z.record(z.string(), costModelSchema$1));
|
|
1773
1797
|
const activityDaySchema = z.object({
|
|
1774
1798
|
tokens: z.number().int().nonnegative(),
|
|
1775
|
-
requests: z.number().int().nonnegative()
|
|
1799
|
+
requests: z.number().int().nonnegative(),
|
|
1800
|
+
cost: costUsageSchema$1.optional()
|
|
1776
1801
|
}).strict();
|
|
1777
1802
|
/** Validate the wire payload before it leaves the host (strict: no drift). */
|
|
1778
1803
|
const contextActivitySchema = z.object({ days: z.record(z.string(), activityDaySchema) }).strict();
|
|
1779
|
-
/** The persisted fold-state schema —
|
|
1780
|
-
const activityStateSchema = z.object({
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
*
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1804
|
+
/** The persisted fold-state schema — validated before a checkpoint row seeds a fold. */
|
|
1805
|
+
const activityStateSchema = z.object({
|
|
1806
|
+
days: z.record(z.string(), activityDaySchema),
|
|
1807
|
+
model: z.string().optional(),
|
|
1808
|
+
provider: z.string().optional(),
|
|
1809
|
+
stepStart: z.number().optional()
|
|
1810
|
+
});
|
|
1811
|
+
/**
|
|
1812
|
+
* One durable usage object's billed buckets, or null when NO bucket is
|
|
1813
|
+
* readable (the same rule the timeline fold bills by: a settlement that
|
|
1814
|
+
* carried no usage still counts its request without fabricating tokens).
|
|
1815
|
+
* Every bucket passes the shared per-bucket sanitizer ({@link tokenCountOf}
|
|
1816
|
+
* — fractions round, negatives clamp, garbage reads absent).
|
|
1817
|
+
*/
|
|
1818
|
+
function billedBucketsOf(value) {
|
|
1819
|
+
if (value === null || typeof value !== "object") return null;
|
|
1791
1820
|
const usage = value;
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1821
|
+
const input = tokenCountOf(usage.inputTokens);
|
|
1822
|
+
const cacheRead = tokenCountOf(usage.cacheReadTokens);
|
|
1823
|
+
const cacheWrite = tokenCountOf(usage.cacheWriteTokens);
|
|
1824
|
+
const output = tokenCountOf(usage.outputTokens);
|
|
1825
|
+
if (input === null && cacheRead === null && cacheWrite === null && output === null) return null;
|
|
1826
|
+
return {
|
|
1827
|
+
input: input ?? 0,
|
|
1828
|
+
cacheRead: cacheRead ?? 0,
|
|
1829
|
+
cacheWrite: cacheWrite ?? 0,
|
|
1830
|
+
output: output ?? 0
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Add one settlement's billed buckets to a day's pricing record — the same
|
|
1835
|
+
* clone-along-the-mutated-path walk the timeline fold's accumulateCost
|
|
1836
|
+
* prices by, so the two folds' records share one shape and one semantics.
|
|
1837
|
+
* The pricing period splits off the SAME instant the day bucket does: a fee
|
|
1838
|
+
* can never land on one day at the neighbouring window's rate.
|
|
1839
|
+
*/
|
|
1840
|
+
function pricedDayOf(prev, provider, model, period, usage) {
|
|
1841
|
+
const models = prev?.[provider] ?? {};
|
|
1842
|
+
const periods = models[model] ?? {};
|
|
1843
|
+
const b = periods[period] ?? {
|
|
1844
|
+
uncached: 0,
|
|
1845
|
+
cacheRead: 0,
|
|
1846
|
+
cacheWrite: 0,
|
|
1847
|
+
output: 0
|
|
1848
|
+
};
|
|
1849
|
+
return {
|
|
1850
|
+
...prev,
|
|
1851
|
+
[provider]: {
|
|
1852
|
+
...models,
|
|
1853
|
+
[model]: {
|
|
1854
|
+
...periods,
|
|
1855
|
+
[period]: {
|
|
1856
|
+
uncached: b.uncached + usage.input,
|
|
1857
|
+
cacheRead: b.cacheRead + usage.cacheRead,
|
|
1858
|
+
cacheWrite: b.cacheWrite + usage.cacheWrite,
|
|
1859
|
+
output: b.output + usage.output
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1804
1862
|
}
|
|
1805
|
-
}
|
|
1806
|
-
return any ? total : 0;
|
|
1863
|
+
};
|
|
1807
1864
|
}
|
|
1808
1865
|
/**
|
|
1809
|
-
* Fold one committed event into the ledger.
|
|
1810
|
-
*
|
|
1811
|
-
*
|
|
1812
|
-
*
|
|
1813
|
-
*
|
|
1866
|
+
* Fold one committed event into the ledger. Four event types advance it:
|
|
1867
|
+
* `request/header` tracks the route in force (the timeline fold's rule —
|
|
1868
|
+
* last header wins), `step/start`/`step/end` maintain the initiation stamp,
|
|
1869
|
+
* and `assistant/message` (the step settlement — both supported log
|
|
1870
|
+
* generations) books the day. Everything else returns the state reference
|
|
1871
|
+
* unchanged, as does a settlement with no attributable instant. The next
|
|
1872
|
+
* state is a copy along the mutated path only — the persisted previous
|
|
1873
|
+
* state is never mutated in place.
|
|
1814
1874
|
*/
|
|
1815
1875
|
function applyActivity(state, event) {
|
|
1876
|
+
if (event.type === "request/header") {
|
|
1877
|
+
const config = event.data?.header?.config;
|
|
1878
|
+
if (config === null || typeof config !== "object") return state;
|
|
1879
|
+
const model = typeof config.model === "string" ? config.model : void 0;
|
|
1880
|
+
const provider = typeof config.provider === "string" ? config.provider : void 0;
|
|
1881
|
+
const nextModel = model !== void 0 ? model : state.model;
|
|
1882
|
+
const nextProvider = provider !== void 0 ? provider : state.provider;
|
|
1883
|
+
if (nextModel === state.model && nextProvider === state.provider) return state;
|
|
1884
|
+
return {
|
|
1885
|
+
...state,
|
|
1886
|
+
...nextModel !== void 0 ? { model: nextModel } : {},
|
|
1887
|
+
...nextProvider !== void 0 ? { provider: nextProvider } : {}
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
if (event.type === "step/start") {
|
|
1891
|
+
if (!Number.isFinite(event.time)) return state;
|
|
1892
|
+
return state.stepStart === event.time ? state : {
|
|
1893
|
+
...state,
|
|
1894
|
+
stepStart: event.time
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
if (event.type === "step/end") {
|
|
1898
|
+
if (state.stepStart === void 0) return state;
|
|
1899
|
+
const next = { ...state };
|
|
1900
|
+
delete next.stepStart;
|
|
1901
|
+
return next;
|
|
1902
|
+
}
|
|
1816
1903
|
if (event.type !== "assistant/message") return state;
|
|
1817
|
-
const
|
|
1904
|
+
const initiated = state.stepStart !== void 0 ? state.stepStart : event.time;
|
|
1905
|
+
const key = dayKeyOf(initiated);
|
|
1818
1906
|
if (key === null) return state;
|
|
1819
1907
|
const data = event.data;
|
|
1820
|
-
const
|
|
1908
|
+
const buckets = billedBucketsOf(data?.usage);
|
|
1821
1909
|
const prev = state.days[key];
|
|
1910
|
+
let cost = prev?.cost;
|
|
1911
|
+
if (buckets !== null && state.model !== void 0) {
|
|
1912
|
+
const provider = state.provider ?? "";
|
|
1913
|
+
const period = isDeepSeekProvider(provider) && !isPeakUtc(initiated) ? "off" : "peak";
|
|
1914
|
+
cost = pricedDayOf(cost, provider, state.model, period, buckets);
|
|
1915
|
+
}
|
|
1822
1916
|
const entry = {
|
|
1823
|
-
tokens: prev === void 0 ?
|
|
1824
|
-
requests: prev === void 0 ?
|
|
1917
|
+
tokens: (prev === void 0 ? 0 : prev.tokens) + (buckets === null ? 0 : buckets.input + buckets.cacheRead + buckets.cacheWrite + buckets.output),
|
|
1918
|
+
requests: (prev === void 0 ? 0 : prev.requests) + 1,
|
|
1919
|
+
...cost !== void 0 ? { cost } : {}
|
|
1825
1920
|
};
|
|
1826
1921
|
let days = {
|
|
1827
1922
|
...state.days,
|
|
@@ -1833,12 +1928,19 @@ function applyActivity(state, event) {
|
|
|
1833
1928
|
const drop = new Set(keys.slice(0, keys.length - MAX_KEPT_DAYS));
|
|
1834
1929
|
days = Object.fromEntries(Object.entries(days).filter(([k]) => !drop.has(k)));
|
|
1835
1930
|
}
|
|
1836
|
-
return {
|
|
1931
|
+
return {
|
|
1932
|
+
...state,
|
|
1933
|
+
days
|
|
1934
|
+
};
|
|
1837
1935
|
}
|
|
1838
1936
|
/**
|
|
1839
1937
|
* The daily-activity projection unit, registered alongside the timeline and
|
|
1840
1938
|
* headers units (host/index.ts); the overview reads it through the session
|
|
1841
|
-
* list's projection column. `stateVersion`
|
|
1939
|
+
* list's projection column. `stateVersion` 2: the ledger gained the per-day
|
|
1940
|
+
* pricing records (and the fold the route/step-start tracking behind them),
|
|
1941
|
+
* so on upgrade every cached row reads version-stale — the dashboard's
|
|
1942
|
+
* warm-up cold-refolds it from the durable log, rebuilding the fees for the
|
|
1943
|
+
* whole corpus exactly.
|
|
1842
1944
|
*/
|
|
1843
1945
|
function createContextActivityDefinition() {
|
|
1844
1946
|
return {
|
|
@@ -1850,7 +1952,7 @@ function createContextActivityDefinition() {
|
|
|
1850
1952
|
viewSchema: contextActivitySchema,
|
|
1851
1953
|
view: (state) => ({ days: Object.fromEntries(Object.entries(state.days).map(([k, v]) => [k, { ...v }])) })
|
|
1852
1954
|
},
|
|
1853
|
-
stateVersion:
|
|
1955
|
+
stateVersion: 2
|
|
1854
1956
|
};
|
|
1855
1957
|
}
|
|
1856
1958
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.1",
|
|
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
|
"icon": "icon.svg",
|
|
6
6
|
"author": "bowenliang123",
|
|
@@ -92,7 +92,8 @@
|
|
|
92
92
|
"@deepseek-ai/dsh-session": ">=0.1.5-rc.1",
|
|
93
93
|
"@deepseek-ai/dsh-settings": ">=0.1.5-rc.1",
|
|
94
94
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
95
|
-
"react": "^18.3.1"
|
|
95
|
+
"react": "^18.3.1",
|
|
96
|
+
"react-dom": "^18.3.1"
|
|
96
97
|
},
|
|
97
98
|
"peerDependenciesMeta": {
|
|
98
99
|
"@deepseek-ai/dsh-client-ui-primitives": {
|
|
@@ -100,13 +101,14 @@
|
|
|
100
101
|
},
|
|
101
102
|
"react": {
|
|
102
103
|
"optional": true
|
|
104
|
+
},
|
|
105
|
+
"react-dom": {
|
|
106
|
+
"optional": true
|
|
103
107
|
}
|
|
104
108
|
},
|
|
105
109
|
"devDependencies": {
|
|
106
110
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
107
111
|
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.1",
|
|
108
|
-
"@deepseek-ai/dsh-invariants": "0.1.5-rc.1",
|
|
109
|
-
"@deepseek-ai/dsh-scope": "0.1.5-rc.1",
|
|
110
112
|
"@deepseek-ai/dsh-session": "0.1.5-rc.1",
|
|
111
113
|
"@deepseek-ai/dsh-session-projection": "0.1.5-rc.1",
|
|
112
114
|
"@deepseek-ai/dsh-settings": "0.1.5-rc.1",
|
|
@@ -117,15 +119,15 @@
|
|
|
117
119
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
118
120
|
"@tailwindcss/node": "4.3.3",
|
|
119
121
|
"@tailwindcss/oxide": "4.3.3",
|
|
120
|
-
"@types/node": "^26.
|
|
122
|
+
"@types/node": "^26.6.3",
|
|
121
123
|
"@types/react": "^18.3.31",
|
|
122
124
|
"@types/react-dom": "^18.3.7",
|
|
123
125
|
"@vitest/coverage-v8": "^4.1.11",
|
|
124
126
|
"anser": "^2.3.5",
|
|
125
127
|
"clsx": "^2.0.0",
|
|
126
|
-
"eslint-plugin-sonarjs": "^4.2.
|
|
128
|
+
"eslint-plugin-sonarjs": "^4.2.1",
|
|
127
129
|
"husky": "^9.1.7",
|
|
128
|
-
"jsdom": "^30.
|
|
130
|
+
"jsdom": "^30.1.1",
|
|
129
131
|
"katex": "^0.16.47",
|
|
130
132
|
"lightningcss": "1.32.0",
|
|
131
133
|
"mdast-util-from-markdown": "^2.0.3",
|
|
@@ -139,17 +141,16 @@
|
|
|
139
141
|
"micromark-util-classify-character": "^2.0.1",
|
|
140
142
|
"micromark-util-sanitize-uri": "^2.0.1",
|
|
141
143
|
"micromark-util-symbol": "^2.0.1",
|
|
142
|
-
"oxlint": "1.
|
|
143
|
-
"oxlint-tsgolint": "7.0.
|
|
144
|
-
"playwright-core": "^1.
|
|
144
|
+
"oxlint": "1.85.0",
|
|
145
|
+
"oxlint-tsgolint": "7.0.2003",
|
|
146
|
+
"playwright-core": "^1.63.0",
|
|
145
147
|
"react": "^18.3.1",
|
|
146
148
|
"react-dom": "^18.3.1",
|
|
147
149
|
"shiki": "^4.3.1",
|
|
148
|
-
"simple-icons": "16.
|
|
150
|
+
"simple-icons": "16.32.0",
|
|
149
151
|
"tailwindcss": "4.3.3",
|
|
150
152
|
"tsdown": "^0.23.0",
|
|
151
153
|
"typescript": "^7.0.2",
|
|
152
|
-
"vitest": "^4.1.11"
|
|
153
|
-
"zod": "^4.4.3"
|
|
154
|
+
"vitest": "^4.1.11"
|
|
154
155
|
}
|
|
155
156
|
}
|