@rayadesu/dsh-llm-billing 0.2.1 → 0.2.2

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.
@@ -0,0 +1,88 @@
1
+ /**
2
+ * `billingTodaySpend` session-projection unit: per-session, per-Beijing-day
3
+ * billed spend, folded eagerly by the DSH projection drive over committed
4
+ * session events and checkpointed by the projection cache. The unit keeps only
5
+ * the spend of the session's LATEST priced day (events are append-only and
6
+ * chronological, so a day strictly older than the state's day never returns);
7
+ * the aggregate "today" read sums the units whose `dayKey` matches the current
8
+ * Beijing day — zero full-log scans once the fold is warm.
9
+ *
10
+ * The unit's fold shares {@link priceEvent} with the events-scan paths
11
+ * (`computeTodaySpend`), so both price with the same table. The unit is
12
+ * client-visible (`wire` = identity) because the persisted-cache read ladder
13
+ * (`sessionProjectionCache.coldSnapshot` / registry `restore`) serves only
14
+ * wired units; the wire value is the state itself.
15
+ * @module @rayadesu/dsh-llm-billing/projection
16
+ */
17
+ import { z } from 'zod';
18
+ import { addEventContribution, emptyTodaySpend, priceEvent } from "./billing.js";
19
+ /** The projection key this unit owns. */
20
+ export const BILLING_UNIT_KEY = 'billingTodaySpend';
21
+ const modelRowSchema = z.object({
22
+ model: z.string(),
23
+ displayName: z.string(),
24
+ cost: z.number().nonnegative(),
25
+ peakCost: z.number().nonnegative(),
26
+ offPeakCost: z.number().nonnegative(),
27
+ cacheHitInputTokens: z.number().int().nonnegative(),
28
+ cacheMissInputTokens: z.number().int().nonnegative(),
29
+ outputTokens: z.number().int().nonnegative(),
30
+ cacheHitInputCost: z.number().nonnegative(),
31
+ cacheMissInputCost: z.number().nonnegative(),
32
+ outputCost: z.number().nonnegative(),
33
+ }).strict();
34
+ const todaySpendSchema = z.object({
35
+ total: z.number().nonnegative(),
36
+ models: z.array(modelRowSchema),
37
+ }).strict();
38
+ const billingUnitSchema = z.object({
39
+ dayKey: z.string(),
40
+ spend: todaySpendSchema,
41
+ }).strict();
42
+ /**
43
+ * Build the `billingTodaySpend` unit for one resolved pricing table. The
44
+ * pricing closure is fixed at registration; a pricing-table change therefore
45
+ * prices only events folded after the change (historical spend keeps its
46
+ * historical rates), unlike the events-scan paths which re-price the whole
47
+ * log. Bump {@link ProjectionDefinition.stateVersion} whenever the state
48
+ * shape or fold semantics change, so persisted checkpoint rows are discarded
49
+ * instead of folded forward.
50
+ * @param billing - resolved pricing with peak-hour windows.
51
+ * @param catalog - model display rows, in presentation order.
52
+ * @returns the unit definition to register on `ctx.sessionProjections`.
53
+ */
54
+ export function billingTodaySpendDefinition(billing, catalog) {
55
+ const names = new Map(catalog.map(model => [model.id, model.name]));
56
+ return {
57
+ key: BILLING_UNIT_KEY,
58
+ stateVersion: 1,
59
+ stateSchema: billingUnitSchema,
60
+ init: () => ({ dayKey: '', spend: emptyTodaySpend() }),
61
+ apply: (state, event) => {
62
+ const priced = priceEvent(event, billing, names);
63
+ if (priced === undefined)
64
+ return state;
65
+ if (state.dayKey === priced.dayKey) {
66
+ return { dayKey: state.dayKey, spend: addEventContribution(state.spend, priced) };
67
+ }
68
+ // The session log is append-only and chronological, so an event whose
69
+ // Beijing day is strictly older than the state's day cannot legally
70
+ // follow it; ignore defensively to keep the persisted fold
71
+ // deterministic under reordered or clock-skewed timestamps.
72
+ if (state.dayKey !== '' && priced.dayKey < state.dayKey)
73
+ return state;
74
+ // First priced event, or the session's first priced event of a new day:
75
+ // the state resets to that day's spend.
76
+ return { dayKey: priced.dayKey, spend: addEventContribution(emptyTodaySpend(), priced) };
77
+ },
78
+ wire: { viewSchema: billingUnitSchema, view: state => state },
79
+ };
80
+ }
81
+ /** Fold a unit from init over one session's event log (the detached cold recipe). */
82
+ export function foldBillingUnit(unit, events) {
83
+ let state = unit.init();
84
+ for (const event of events)
85
+ state = unit.apply(state, event);
86
+ return state;
87
+ }
88
+ //# sourceMappingURL=projection.js.map
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Today-spend read path: the 60-second Beijing-day cache with in-flight
3
+ * coalescing and a force bypass (plan A1), plus the two scan strategies that
4
+ * compute the aggregate behind a cache miss:
5
+ *
6
+ * - projection path (plan C): live sessions read their eagerly folded
7
+ * `billingTodaySpend` projection cell; cold sessions resolve through the
8
+ * projection-cache ladder (cached row + tail replay + registry restore,
9
+ * with write-back) or, without the cache service, one detached local fold
10
+ * over a full `inspect`. Persisted revisions gate every cold read, so a
11
+ * session whose log did not change since the last resolution costs nothing.
12
+ * - events path (plans A2/A3): collect only today's events (per-event
13
+ * Beijing-day filter during collection) with a hard cap, skipping sessions
14
+ * whose persisted revision is unchanged since the last scan.
15
+ *
16
+ * Both strategies run behind the same {@link TodaySpendCache}, so a miss
17
+ * happens at most once per 60 seconds per process, and a manual refresh
18
+ * (`force`) bypasses the time window but keeps the revision caches — an
19
+ * unchanged log provably cannot change the aggregate.
20
+ * @module @rayadesu/dsh-llm-billing/today-spend
21
+ */
22
+ import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session';
23
+ import type { SessionPersistenceRevision } from '@deepseek-ai/dsh-session-persistence';
24
+ import type { ResolvedBilling } from './billing.ts';
25
+ import type { DeepSeekTodaySpend } from './types.ts';
26
+ import { BILLING_UNIT_KEY, type BillingUnitState } from './projection.ts';
27
+ import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection';
28
+ /** Structural slice of a live session the scanner reads. */
29
+ export interface ScannerSession {
30
+ readonly id: SessionId;
31
+ readonly events: readonly SessionEvent[];
32
+ }
33
+ /** Structural slice of a listed persisted session. */
34
+ export interface ScannerPersistedHeader {
35
+ readonly id: SessionId;
36
+ }
37
+ /** Structural slices of the optional services the scanner reads through. */
38
+ export interface TodaySpendScannerDeps {
39
+ /** Resolves the live SessionStore at scan time (absent in headless assemblies). */
40
+ sessions?: () => {
41
+ list(): readonly ScannerSession[];
42
+ } | undefined;
43
+ /** Resolves the persistence backend at scan time (absent without persistence). */
44
+ persistence?: () => {
45
+ listSnapshots(): Promise<readonly {
46
+ header: ScannerPersistedHeader;
47
+ revision: SessionPersistenceRevision;
48
+ }[]>;
49
+ inspect(id: SessionId): Promise<{
50
+ events: readonly SessionEvent[];
51
+ }>;
52
+ } | undefined;
53
+ /** Resolves the session-projection registry at scan time (absent → events path). */
54
+ projections?: () => {
55
+ stateOf(session: ScannerSession, key: typeof BILLING_UNIT_KEY): BillingUnitState | undefined;
56
+ } | undefined;
57
+ /** Resolves the projection cache at scan time (absent → detached fold for cold sessions). */
58
+ projectionCache?: () => {
59
+ coldSnapshot(id: SessionId): Promise<{
60
+ values: Partial<Record<typeof BILLING_UNIT_KEY, BillingUnitState>>;
61
+ }>;
62
+ } | undefined;
63
+ /**
64
+ * Registers the billing unit on the projection registry, called once before
65
+ * the first projection-path scan. The registry builds cells lazily over the
66
+ * in-memory log, so events committed before registration are folded on
67
+ * first touch — late registration is safe by design.
68
+ */
69
+ ensureUnit?: () => void;
70
+ /** The billing unit's fold (the projection path's detached cold recipe). */
71
+ unit: Pick<ProjectionDefinition<'billingTodaySpend', BillingUnitState>, 'init' | 'apply'>;
72
+ /** Hard cap on today's events collected by the events path. */
73
+ maxEvents: number;
74
+ /** Warn sink for truncation and unreadable sessions. */
75
+ logger: {
76
+ warn(message: string): void;
77
+ };
78
+ /** Pricing table resolved from the plugin config. */
79
+ billing: ResolvedBilling;
80
+ /** Model display rows, in presentation order. */
81
+ catalog: readonly {
82
+ id: string;
83
+ name: string;
84
+ }[];
85
+ }
86
+ /**
87
+ * The A1 cache: one Beijing-day key + a 60s window, an in-flight promise that
88
+ * coalesces concurrent misses, and a `force` bypass for the manual refresh
89
+ * path. Cross-day invalidation is automatic (the day key changes); a failed
90
+ * scan leaves the previous value in place and retries on the next call.
91
+ */
92
+ export declare class TodaySpendCache {
93
+ private readonly scan;
94
+ private readonly ttlMs;
95
+ private readonly now;
96
+ private cachedDayKey;
97
+ private cachedValue;
98
+ private cachedAt;
99
+ private inFlight;
100
+ /**
101
+ * @param ttlMs - time window in milliseconds (default 60 000).
102
+ * @param now - clock source (injectable for tests).
103
+ * @param scan - the aggregate computation behind a miss.
104
+ */
105
+ constructor(scan: (dayKey: string) => Promise<DeepSeekTodaySpend>, ttlMs?: number, now?: () => Date);
106
+ /**
107
+ * Read today's spend, cached per Beijing day within the TTL window.
108
+ * @param force - bypass the time window (manual refresh); the day-key gate
109
+ * and the in-flight coalescing still apply to non-force callers.
110
+ * @returns today's spend.
111
+ */
112
+ get(force?: boolean): Promise<DeepSeekTodaySpend>;
113
+ }
114
+ /**
115
+ * The aggregate computation behind a cache miss. Chooses the projection path
116
+ * when the projection registry is composed, the events path otherwise; both
117
+ * gate cold reads on persisted revisions so steady-state scans touch only
118
+ * sessions whose logs actually changed.
119
+ */
120
+ export declare class TodaySpendScanner {
121
+ private readonly deps;
122
+ /** Cold sessions resolved on the projection path: id → revision + unit state. */
123
+ private readonly coldResolved;
124
+ /** Cold sessions resolved on the events path: id → revision (events were collected). */
125
+ private lastEventsScan;
126
+ constructor(deps: TodaySpendScannerDeps);
127
+ /**
128
+ * Compute today's aggregate for one Beijing day.
129
+ * @param dayKey - the Beijing-time calendar-day key to aggregate.
130
+ * @returns today's spend across every session.
131
+ */
132
+ scan(dayKey: string): Promise<DeepSeekTodaySpend>;
133
+ /** Projection path: eager cells for live sessions, revision-gated cold ladder for the rest. */
134
+ private scanProjections;
135
+ /** Events path: collect only today's events (capped), gated by revisions. */
136
+ private scanEvents;
137
+ }
138
+ //# sourceMappingURL=today-spend.d.ts.map
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Today-spend read path: the 60-second Beijing-day cache with in-flight
3
+ * coalescing and a force bypass (plan A1), plus the two scan strategies that
4
+ * compute the aggregate behind a cache miss:
5
+ *
6
+ * - projection path (plan C): live sessions read their eagerly folded
7
+ * `billingTodaySpend` projection cell; cold sessions resolve through the
8
+ * projection-cache ladder (cached row + tail replay + registry restore,
9
+ * with write-back) or, without the cache service, one detached local fold
10
+ * over a full `inspect`. Persisted revisions gate every cold read, so a
11
+ * session whose log did not change since the last resolution costs nothing.
12
+ * - events path (plans A2/A3): collect only today's events (per-event
13
+ * Beijing-day filter during collection) with a hard cap, skipping sessions
14
+ * whose persisted revision is unchanged since the last scan.
15
+ *
16
+ * Both strategies run behind the same {@link TodaySpendCache}, so a miss
17
+ * happens at most once per 60 seconds per process, and a manual refresh
18
+ * (`force`) bypasses the time window but keeps the revision caches — an
19
+ * unchanged log provably cannot change the aggregate.
20
+ * @module @rayadesu/dsh-llm-billing/today-spend
21
+ */
22
+ import { beijingDayKey, computeTodaySpend, emptyTodaySpend, mergeTodaySpend } from "./billing.js";
23
+ import { BILLING_UNIT_KEY, foldBillingUnit } from "./projection.js";
24
+ /** Bounded parallel fan-out: run `run` over `items` with at most `limit` in flight. */
25
+ async function withConcurrency(items, limit, run) {
26
+ const queue = [...items];
27
+ await Promise.all(Array.from({ length: Math.min(limit, queue.length) }, async () => {
28
+ for (let job = queue.shift(); job !== undefined; job = queue.shift())
29
+ await run(job);
30
+ }));
31
+ }
32
+ /**
33
+ * The A1 cache: one Beijing-day key + a 60s window, an in-flight promise that
34
+ * coalesces concurrent misses, and a `force` bypass for the manual refresh
35
+ * path. Cross-day invalidation is automatic (the day key changes); a failed
36
+ * scan leaves the previous value in place and retries on the next call.
37
+ */
38
+ export class TodaySpendCache {
39
+ scan;
40
+ ttlMs;
41
+ now;
42
+ cachedDayKey;
43
+ cachedValue;
44
+ cachedAt = 0;
45
+ inFlight;
46
+ /**
47
+ * @param ttlMs - time window in milliseconds (default 60 000).
48
+ * @param now - clock source (injectable for tests).
49
+ * @param scan - the aggregate computation behind a miss.
50
+ */
51
+ constructor(scan, ttlMs = 60_000, now = () => new Date()) {
52
+ this.scan = scan;
53
+ this.ttlMs = ttlMs;
54
+ this.now = now;
55
+ }
56
+ /**
57
+ * Read today's spend, cached per Beijing day within the TTL window.
58
+ * @param force - bypass the time window (manual refresh); the day-key gate
59
+ * and the in-flight coalescing still apply to non-force callers.
60
+ * @returns today's spend.
61
+ */
62
+ get(force = false) {
63
+ const now = this.now();
64
+ const dayKey = beijingDayKey(now);
65
+ if (!force && this.cachedDayKey === dayKey && this.cachedValue !== undefined
66
+ && now.getTime() - this.cachedAt < this.ttlMs) {
67
+ return Promise.resolve(this.cachedValue);
68
+ }
69
+ if (!force && this.inFlight !== undefined)
70
+ return this.inFlight;
71
+ const run = (async () => {
72
+ try {
73
+ const value = await this.scan(dayKey);
74
+ this.cachedDayKey = dayKey;
75
+ this.cachedValue = value;
76
+ this.cachedAt = now.getTime();
77
+ return value;
78
+ }
79
+ finally {
80
+ this.inFlight = undefined;
81
+ }
82
+ })();
83
+ if (!force)
84
+ this.inFlight = run;
85
+ return run;
86
+ }
87
+ }
88
+ /**
89
+ * The aggregate computation behind a cache miss. Chooses the projection path
90
+ * when the projection registry is composed, the events path otherwise; both
91
+ * gate cold reads on persisted revisions so steady-state scans touch only
92
+ * sessions whose logs actually changed.
93
+ */
94
+ export class TodaySpendScanner {
95
+ deps;
96
+ /** Cold sessions resolved on the projection path: id → revision + unit state. */
97
+ coldResolved = new Map();
98
+ /** Cold sessions resolved on the events path: id → revision (events were collected). */
99
+ lastEventsScan;
100
+ constructor(deps) {
101
+ this.deps = deps;
102
+ }
103
+ /**
104
+ * Compute today's aggregate for one Beijing day.
105
+ * @param dayKey - the Beijing-time calendar-day key to aggregate.
106
+ * @returns today's spend across every session.
107
+ */
108
+ async scan(dayKey) {
109
+ if (this.deps.projections?.() === undefined)
110
+ return this.scanEvents(dayKey);
111
+ this.deps.ensureUnit?.();
112
+ return this.scanProjections(dayKey);
113
+ }
114
+ /** Projection path: eager cells for live sessions, revision-gated cold ladder for the rest. */
115
+ async scanProjections(dayKey) {
116
+ const { sessions, persistence, projections, projectionCache, unit, logger } = this.deps;
117
+ let total = emptyTodaySpend();
118
+ const liveIds = new Set();
119
+ if (sessions !== undefined) {
120
+ const store = sessions();
121
+ if (store !== undefined) {
122
+ for (const session of store.list()) {
123
+ liveIds.add(session.id);
124
+ const state = projections?.()?.stateOf(session, BILLING_UNIT_KEY);
125
+ if (state !== undefined && state.dayKey === dayKey) {
126
+ total = mergeTodaySpend(total, state.spend);
127
+ }
128
+ }
129
+ }
130
+ }
131
+ const persistenceService = persistence?.();
132
+ if (persistenceService === undefined)
133
+ return total;
134
+ const snapshots = await persistenceService.listSnapshots();
135
+ const pending = [];
136
+ for (const { header, revision } of snapshots) {
137
+ if (liveIds.has(header.id))
138
+ continue;
139
+ const resolved = this.coldResolved.get(header.id);
140
+ if (resolved !== undefined && resolved.revision === revision) {
141
+ if (resolved.value.dayKey === dayKey)
142
+ total = mergeTodaySpend(total, resolved.value.spend);
143
+ continue;
144
+ }
145
+ pending.push({ id: header.id, revision });
146
+ }
147
+ await withConcurrency(pending, 8, async ({ id, revision }) => {
148
+ let value;
149
+ const cache = projectionCache?.();
150
+ if (cache !== undefined) {
151
+ try {
152
+ value = (await cache.coldSnapshot(id)).values[BILLING_UNIT_KEY];
153
+ }
154
+ catch (error) {
155
+ logger.warn(`llm-billing: projection cold read for session ${id} failed: ${String(error)}`);
156
+ }
157
+ }
158
+ if (value === undefined) {
159
+ try {
160
+ const inspection = await persistenceService.inspect(id);
161
+ value = foldBillingUnit(unit, inspection.events);
162
+ }
163
+ catch (error) {
164
+ // One unreadable session must not blank the whole-day aggregate.
165
+ logger.warn(`llm-billing: skipping unreadable session ${id}: ${String(error)}`);
166
+ }
167
+ }
168
+ if (value !== undefined)
169
+ this.coldResolved.set(id, { revision, value });
170
+ });
171
+ for (const { id } of pending) {
172
+ const resolved = this.coldResolved.get(id);
173
+ if (resolved !== undefined && resolved.value.dayKey === dayKey) {
174
+ total = mergeTodaySpend(total, resolved.value.spend);
175
+ }
176
+ }
177
+ return total;
178
+ }
179
+ /** Events path: collect only today's events (capped), gated by revisions. */
180
+ async scanEvents(dayKey) {
181
+ const { sessions, persistence, maxEvents, logger, billing, catalog } = this.deps;
182
+ const events = [];
183
+ const liveIds = new Set();
184
+ let truncated = false;
185
+ if (sessions !== undefined) {
186
+ const store = sessions();
187
+ if (store !== undefined) {
188
+ for (const session of store.list()) {
189
+ liveIds.add(session.id);
190
+ for (const event of session.events) {
191
+ if (beijingDayKey(new Date(event.time)) !== dayKey)
192
+ continue;
193
+ events.push(event);
194
+ if (events.length >= maxEvents) {
195
+ truncated = true;
196
+ break;
197
+ }
198
+ }
199
+ if (truncated)
200
+ break;
201
+ }
202
+ }
203
+ }
204
+ const persistenceService = persistence?.();
205
+ if (!truncated && persistenceService !== undefined) {
206
+ const snapshots = await persistenceService.listSnapshots();
207
+ for (const { header, revision } of snapshots) {
208
+ if (liveIds.has(header.id))
209
+ continue;
210
+ if (this.lastEventsScan?.get(header.id) === revision)
211
+ continue;
212
+ try {
213
+ const inspection = await persistenceService.inspect(header.id);
214
+ for (const event of inspection.events) {
215
+ if (beijingDayKey(new Date(event.time)) !== dayKey)
216
+ continue;
217
+ events.push(event);
218
+ if (events.length >= maxEvents) {
219
+ truncated = true;
220
+ break;
221
+ }
222
+ }
223
+ }
224
+ catch (error) {
225
+ // One unreadable session must not blank the whole-day aggregate.
226
+ logger.warn(`llm-billing: skipping unreadable session ${header.id}: ${String(error)}`);
227
+ }
228
+ if (truncated)
229
+ break;
230
+ }
231
+ // Only a complete pass may advance the revision watermark: a truncated
232
+ // pass left sessions unread, and recording them would skip their events
233
+ // on the next scan.
234
+ if (!truncated) {
235
+ this.lastEventsScan = new Map(snapshots.map(snapshot => [snapshot.header.id, snapshot.revision]));
236
+ }
237
+ }
238
+ if (truncated)
239
+ logger.warn(`llm-billing: today's events exceeded ${maxEvents}; result truncated`);
240
+ // The reference moment is derived from the day key (UTC midnight on that
241
+ // date is 08:00 Beijing the same day), so the pricing re-check cannot
242
+ // drift from the collection filter across a Beijing-day boundary.
243
+ return computeTodaySpend(events, billing, catalog, new Date(`${dayKey}T00:00:00Z`));
244
+ }
245
+ }
246
+ //# sourceMappingURL=today-spend.js.map
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Client-safe balance and spend vocabulary shared by the `billing` Remote,
3
3
  * its generated artifacts, and the web UI.
4
- * @module @deepseek-ai/dsh-llm-billing/types
4
+ * @module @rayadesu/dsh-llm-billing/types
5
5
  */
6
6
  /** One currency line of the account balance returned by `GET /user/balance`. */
7
7
  export interface DeepSeekBalanceLine {
@@ -46,14 +46,14 @@ export interface DeepSeekSessionSpendModel {
46
46
  /** Billed cost of output tokens (reasoning included) in CNY. */
47
47
  outputCost: number;
48
48
  }
49
- /** The billed spend of one session, priced per event by its Beijing-time peak/off-peak hour. */
49
+ /** The billed spend of one session, priced per event by its Beijing-time hour and weekday (peak hours apply Monday–Friday only; weekends are off-peak). */
50
50
  export interface DeepSeekSessionSpend {
51
51
  /** Total billed cost in CNY across every priced model. */
52
52
  total: number;
53
53
  /** One row per model that reported usage AND has a pricing row; empty when the session has no priced usage. */
54
54
  models: readonly DeepSeekSessionSpendModel[];
55
55
  }
56
- /** The billed spend of every session on one Beijing-time calendar day, priced per event by its peak/off-peak hour. */
56
+ /** The billed spend of every session on one Beijing-time calendar day, priced per event by its Beijing-time hour and weekday (peak hours apply Monday–Friday only; weekends are off-peak). */
57
57
  export interface DeepSeekTodaySpend {
58
58
  /** Total billed cost in CNY across every priced model and every session. */
59
59
  total: number;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Client-safe balance and spend vocabulary shared by the `billing` Remote,
3
3
  * its generated artifacts, and the web UI.
4
- * @module @deepseek-ai/dsh-llm-billing/types
4
+ * @module @rayadesu/dsh-llm-billing/types
5
5
  */
6
6
  export {};
7
7
  //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rayadesu/dsh-llm-billing",
3
3
  "description": "Standalone DeepSeek account-balance and session-spend provider exposed through the billing Remote",
4
- "version": "0.2.1",
4
+ "version": "0.2.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -47,16 +47,19 @@
47
47
  "lib/typert.remote-client.js",
48
48
  "lib/typert.remote-client.d.ts"
49
49
  ],
50
+ "scripts": {
51
+ "prepublishOnly": "node ../../scripts/verify-packages.mjs"
52
+ },
50
53
  "license": "MIT",
51
54
  "peerDependencies": {
52
- "@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
53
- "@deepseek-ai/dsh-launch-environment": "^0.1.0-rc.8",
54
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
55
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
56
- "@deepseek-ai/dsh-session": "^0.1.0-rc.8",
57
- "@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.8",
58
- "@deepseek-ai/dsh-session-projection": "^0.1.0-rc.8",
59
- "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8",
55
+ "@deepseek-ai/dsh-credentials": "^0.1.1-rc.2",
56
+ "@deepseek-ai/dsh-launch-environment": "^0.1.1-rc.2",
57
+ "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
58
+ "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
59
+ "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
60
+ "@deepseek-ai/dsh-session-persistence": "^0.1.1-rc.2",
61
+ "@deepseek-ai/dsh-session-projection": "^0.1.1-rc.2",
62
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
60
63
  "@deepseek-ai/cordis": "^4.0.1"
61
64
  },
62
65
  "dependencies": {
@@ -64,14 +67,14 @@
64
67
  "zod": "^4.4.3"
65
68
  },
66
69
  "devDependencies": {
67
- "@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
68
- "@deepseek-ai/dsh-launch-environment": "^0.1.0-rc.8",
69
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
70
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
71
- "@deepseek-ai/dsh-session": "^0.1.0-rc.8",
72
- "@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.8",
73
- "@deepseek-ai/dsh-session-projection": "^0.1.0-rc.8",
74
- "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8",
70
+ "@deepseek-ai/dsh-credentials": "^0.1.1-rc.2",
71
+ "@deepseek-ai/dsh-launch-environment": "^0.1.1-rc.2",
72
+ "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
73
+ "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
74
+ "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
75
+ "@deepseek-ai/dsh-session-persistence": "^0.1.1-rc.2",
76
+ "@deepseek-ai/dsh-session-projection": "^0.1.1-rc.2",
77
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
75
78
  "@deepseek-ai/cordis": "^4.0.1"
76
79
  }
77
80
  }