dsh-watcher 0.4.0-insights.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.
@@ -0,0 +1,545 @@
1
+ import { z } from "zod";
2
+ import { createHash } from "node:crypto";
3
+ //#region src/insights/engine.mjs
4
+ /** Read-only DSH 0.1.5-rc.2 log fold. No prompt, reasoning or tool body is retained. */
5
+ const record = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
6
+ const count = (v) => Number.isSafeInteger(v) && v >= 0;
7
+ const elapsed = (start, end) => start === null ? 0 : Math.max(0, end - start);
8
+ function emptyStats() {
9
+ return {
10
+ calls: 0,
11
+ reported: 0,
12
+ exactTotals: 0,
13
+ tokens: 0,
14
+ input: 0,
15
+ output: 0,
16
+ cacheRead: 0,
17
+ cacheWrite: 0,
18
+ reasoning: 0,
19
+ reasoningReports: 0,
20
+ modelMs: 0,
21
+ timedCalls: 0,
22
+ firstMs: 0,
23
+ firstSamples: 0,
24
+ reasoningMs: 0,
25
+ tools: 0,
26
+ toolErrors: 0,
27
+ toolMs: 0,
28
+ bashMs: 0,
29
+ retries: 0
30
+ };
31
+ }
32
+ function initialState(header = {}, inherited = 0) {
33
+ const s = {
34
+ version: 1,
35
+ sessionId: String(header.id ?? ""),
36
+ skip: inherited,
37
+ seq: -1,
38
+ updatedAt: 0,
39
+ route: {
40
+ provider: "unknown",
41
+ model: "unknown"
42
+ },
43
+ totals: emptyStats(),
44
+ models: [],
45
+ turn: null,
46
+ open: null,
47
+ tools: [],
48
+ previousFailure: null,
49
+ findings: [],
50
+ findingCount: 0
51
+ };
52
+ s.wire = buildView(s);
53
+ return s;
54
+ }
55
+ function normalizeUsage(value) {
56
+ if (!record(value) || !count(value.inputTokens) || !count(value.outputTokens)) return null;
57
+ if ([
58
+ "cacheReadTokens",
59
+ "cacheWriteTokens",
60
+ "reasoningTokens",
61
+ "totalTokens"
62
+ ].some((k) => value[k] !== void 0 && !count(value[k]))) return null;
63
+ const minimum = value.inputTokens + value.outputTokens + (value.cacheReadTokens ?? 0) + (value.cacheWriteTokens ?? 0);
64
+ if (!Number.isSafeInteger(minimum) || value.totalTokens !== void 0 && value.totalTokens < minimum) return null;
65
+ return {
66
+ input: value.inputTokens,
67
+ output: value.outputTokens,
68
+ cacheRead: value.cacheReadTokens ?? 0,
69
+ cacheWrite: value.cacheWriteTokens ?? 0,
70
+ reasoning: value.reasoningTokens ?? null,
71
+ tokens: value.totalTokens ?? minimum,
72
+ exact: value.totalTokens !== void 0
73
+ };
74
+ }
75
+ function canonical(v) {
76
+ if (Array.isArray(v)) return v.map(canonical);
77
+ if (record(v)) return Object.fromEntries(Object.keys(v).sort().map((k) => [k, canonical(v[k])]));
78
+ return v;
79
+ }
80
+ function fingerprint(v) {
81
+ try {
82
+ let text = typeof v === "string" ? v : JSON.stringify(v);
83
+ if (!text || text.length > 65536) return null;
84
+ try {
85
+ text = JSON.stringify(canonical(JSON.parse(text)));
86
+ } catch {}
87
+ return createHash("sha256").update(text).digest("hex");
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+ function signatureOf(name, args) {
93
+ const hash = fingerprint(args);
94
+ return hash === null ? null : fingerprint([name, hash]);
95
+ }
96
+ function modelRow(s, route) {
97
+ const effort = typeof route?.effort === "string" ? route.effort : null;
98
+ let row = s.models.find((m) => m.provider === route.provider && m.model === route.model && (m.effort ?? null) === effort);
99
+ if (!row) {
100
+ row = {
101
+ provider: route.provider,
102
+ model: route.model,
103
+ ...effort ? { effort } : {},
104
+ ...emptyStats()
105
+ };
106
+ s.models.push(row);
107
+ }
108
+ return row;
109
+ }
110
+ function books(s, route = s.route) {
111
+ return [
112
+ s.totals,
113
+ modelRow(s, route),
114
+ ...s.turn ? [s.turn.stats] : []
115
+ ];
116
+ }
117
+ function begin(s, d, time, uncertain = false) {
118
+ s.open = {
119
+ turn: d.turn,
120
+ step: d.step,
121
+ start: uncertain ? null : time,
122
+ first: null,
123
+ reasoningFirst: null,
124
+ reasoningLast: null,
125
+ lastContentAt: null,
126
+ usage: null,
127
+ route: { ...s.route }
128
+ };
129
+ }
130
+ function settle(s, time, usage, source) {
131
+ const a = s.open;
132
+ if (!a) return;
133
+ const route = record(source) && typeof source.provider === "string" && typeof source.model === "string" ? {
134
+ provider: source.provider,
135
+ model: source.model,
136
+ ...a.route?.effort ? { effort: a.route.effort } : {}
137
+ } : a.route;
138
+ s.route = route;
139
+ const u = usage === void 0 || usage === null ? a.usage : normalizeUsage(usage);
140
+ for (const b of books(s, route)) {
141
+ b.calls++;
142
+ if (a.start !== null) {
143
+ b.timedCalls++;
144
+ b.modelMs += elapsed(a.start, time);
145
+ }
146
+ if (a.start !== null && a.first !== null) {
147
+ b.firstSamples++;
148
+ b.firstMs += elapsed(a.start, a.first);
149
+ }
150
+ if (a.reasoningFirst !== null && a.reasoningLast !== null) b.reasoningMs += elapsed(a.reasoningFirst, a.reasoningLast);
151
+ if (u) {
152
+ b.reported++;
153
+ b.exactTotals += Number(u.exact);
154
+ b.tokens += u.tokens;
155
+ for (const k of [
156
+ "input",
157
+ "output",
158
+ "cacheRead",
159
+ "cacheWrite"
160
+ ]) b[k] += u[k];
161
+ if (u.reasoning !== null) {
162
+ b.reasoningReports++;
163
+ b.reasoning += u.reasoning;
164
+ }
165
+ }
166
+ }
167
+ s.open = null;
168
+ }
169
+ function applyChunk(open, chunk, time) {
170
+ if (!record(chunk) || typeof chunk.type !== "string") return;
171
+ if (chunk.type === "usage") {
172
+ open.usage = normalizeUsage(chunk.usage);
173
+ return;
174
+ }
175
+ if (![
176
+ "reasoning-delta",
177
+ "text-delta",
178
+ "tool-call-delta"
179
+ ].includes(chunk.type)) return;
180
+ if (!(typeof chunk.text === "string" && chunk.text.length) && !(typeof chunk.argumentsDelta === "string" && chunk.argumentsDelta.length) && !chunk.name) return;
181
+ open.first ??= time;
182
+ open.lastContentAt = time;
183
+ if (chunk.type === "reasoning-delta") {
184
+ open.reasoningFirst ??= time;
185
+ open.reasoningLast = time;
186
+ }
187
+ }
188
+ function applyStream(open, stream) {
189
+ if (!open || !Array.isArray(stream)) return;
190
+ for (const rec of stream) {
191
+ if (!record(rec) || typeof rec.type !== "string") continue;
192
+ if (rec.type === "chunk") {
193
+ applyChunk(open, rec.chunk, rec.time);
194
+ continue;
195
+ }
196
+ const parts = rec.type === "tool-call-chunks" ? rec.args : rec.texts;
197
+ const gaps = rec.dt;
198
+ if (!Array.isArray(parts) || !parts.length || !parts.every((p) => typeof p === "string") || !Array.isArray(gaps) || gaps.length !== parts.length - 1 || !gaps.every(Number.isSafeInteger) || !Number.isSafeInteger(rec.time0)) continue;
199
+ let time = rec.time0;
200
+ for (let i = 0; i < parts.length; i++) {
201
+ if (i > 0) time += gaps[i - 1];
202
+ if (rec.type === "reasoning-chunks") applyChunk(open, {
203
+ type: "reasoning-delta",
204
+ text: parts[i]
205
+ }, time);
206
+ else if (rec.type === "text-chunks") applyChunk(open, {
207
+ type: "text-delta",
208
+ text: parts[i]
209
+ }, time);
210
+ else if (rec.type === "tool-call-chunks") applyChunk(open, {
211
+ type: "tool-call-delta",
212
+ argumentsDelta: parts[i],
213
+ name: rec.name
214
+ }, time);
215
+ }
216
+ }
217
+ }
218
+ const interesting = /* @__PURE__ */ new Set([
219
+ "request/header",
220
+ "user/message",
221
+ "turn/start",
222
+ "step/start",
223
+ "assistant/attempt",
224
+ "assistant/message",
225
+ "llm/retry",
226
+ "tool/call",
227
+ "tool/result",
228
+ "step/end",
229
+ "turn/end"
230
+ ]);
231
+ function reduceEvent(state, event) {
232
+ if (!record(event) || !interesting.has(event.type) || !count(event.seq) || !Number.isFinite(event.time) || !record(event.data)) return state;
233
+ if (event.seq <= state.seq) return state;
234
+ const d = event.data;
235
+ if (event.seq < state.skip && event.type !== "request/header") return state;
236
+ if (event.type === "user/message" && !state.previousFailure) return state;
237
+ if (!["request/header", "user/message"].includes(event.type) && !count(d.turn)) return state;
238
+ if (![
239
+ "request/header",
240
+ "user/message",
241
+ "turn/start",
242
+ "turn/end"
243
+ ].includes(event.type) && !count(d.step)) return state;
244
+ if ((event.type === "assistant/attempt" || event.type === "assistant/message") && d.stream !== void 0 && !Array.isArray(d.stream)) return state;
245
+ if (event.type === "assistant/attempt" && (!state.open || state.open.turn !== d.turn || state.open.step !== d.step)) return state;
246
+ const s = structuredClone(state);
247
+ s.wire = state.wire;
248
+ s.seq = event.seq;
249
+ s.updatedAt = event.time;
250
+ if (event.type === "request/header") {
251
+ const config = d.header?.config;
252
+ if (typeof config?.provider === "string" && typeof config.model === "string") {
253
+ const effort = typeof config.reasoningEffort === "string" ? config.reasoningEffort : null;
254
+ s.route = {
255
+ provider: config.provider,
256
+ model: config.model,
257
+ ...effort ? { effort } : {}
258
+ };
259
+ if (s.open) s.open.route = { ...s.route };
260
+ if (s.turn) s.turn.route = { ...s.route };
261
+ }
262
+ } else if (event.type === "user/message") s.previousFailure = null;
263
+ else if (event.type === "turn/start") {
264
+ if (s.open) settle(s, event.time, null, null);
265
+ s.turn = {
266
+ number: d.turn,
267
+ startedAt: event.time,
268
+ endedAt: null,
269
+ steps: 0,
270
+ stats: emptyStats(),
271
+ route: { ...s.route }
272
+ };
273
+ s.previousFailure = null;
274
+ s.tools = [];
275
+ } else if (event.type === "step/start") {
276
+ if (s.open) settle(s, event.time, null, null);
277
+ begin(s, d, event.time);
278
+ if (s.turn) s.turn.steps++;
279
+ } else if (event.type === "assistant/attempt") applyStream(s.open, d.stream);
280
+ else if (event.type === "assistant/message") {
281
+ if (s.open?.turn === d.turn && s.open.step === d.step) {
282
+ applyStream(s.open, d.stream);
283
+ settle(s, event.time, d.usage, d.message?.source);
284
+ }
285
+ } else if (event.type === "llm/retry") {
286
+ if (s.open?.turn === d.turn && s.open.step === d.step) {
287
+ settle(s, event.time, null, null);
288
+ for (const b of books(s)) b.retries++;
289
+ begin(s, d, event.time, true);
290
+ }
291
+ } else if (event.type === "tool/call") {
292
+ const id = d.callId, name = d.name;
293
+ if (typeof id === "string" && typeof name === "string" && !s.tools.some((t) => t.id === id)) {
294
+ s.tools.push({
295
+ id,
296
+ name,
297
+ start: event.time,
298
+ seq: event.seq,
299
+ step: d.step,
300
+ route: { ...s.route },
301
+ signature: signatureOf(name, d.arguments)
302
+ });
303
+ for (const b of books(s)) b.tools++;
304
+ }
305
+ } else if (event.type === "tool/result") {
306
+ const block = d.message?.content?.find?.((b) => b?.type === "tool-result");
307
+ const id = block?.toolCallId ?? d.message?.source?.callId;
308
+ const index = s.tools.findIndex((t) => t.id === id);
309
+ if (index >= 0) {
310
+ const t = s.tools.splice(index, 1)[0];
311
+ const failed = block?.isError === true || record(d.error);
312
+ const dur = elapsed(t.start, event.time);
313
+ for (const b of books(s, t.route)) {
314
+ b.toolMs += dur;
315
+ b.toolErrors += Number(failed);
316
+ if (t.name === "bash") b.bashMs = (b.bashMs ?? 0) + dur;
317
+ }
318
+ const resultHash = block ? fingerprint({
319
+ content: block.content,
320
+ isError: failed,
321
+ code: d.error?.code ?? null
322
+ }) : null;
323
+ const prev = s.previousFailure;
324
+ if (failed && t.signature && resultHash) {
325
+ const same = prev?.signature === t.signature && prev?.resultHash === resultHash && prev?.turn === d.turn;
326
+ const failure = {
327
+ signature: t.signature,
328
+ resultHash,
329
+ turn: d.turn,
330
+ seqs: [...same ? prev.seqs : [], t.seq].slice(-10),
331
+ steps: [...same ? prev.steps : [], t.step].slice(-10)
332
+ };
333
+ s.previousFailure = failure;
334
+ if (failure.seqs.length >= 3) {
335
+ const id = `repeat:${d.turn}:${failure.seqs[0]}`;
336
+ const finding = {
337
+ id,
338
+ kind: "repeated-failure",
339
+ turn: d.turn,
340
+ seqs: failure.seqs,
341
+ steps: failure.steps,
342
+ title: `同一操作连续失败 ${failure.seqs.length} 次`,
343
+ detail: "参数与错误结果指纹相同;这是重复失败证据,不是确定的空转或质量判决。"
344
+ };
345
+ const old = s.findings.findIndex((f) => f.id === id);
346
+ if (old >= 0) s.findings[old] = finding;
347
+ else {
348
+ s.findingCount++;
349
+ s.findings.push(finding);
350
+ s.findings = s.findings.slice(-30);
351
+ }
352
+ }
353
+ } else s.previousFailure = null;
354
+ }
355
+ } else if (event.type === "step/end") {
356
+ if (s.open?.turn === d.turn && s.open.step === d.step) settle(s, event.time, null, null);
357
+ } else if (event.type === "turn/end") {
358
+ if (s.open) settle(s, event.time, null, null);
359
+ if (s.turn) s.turn.endedAt = event.time;
360
+ s.tools = [];
361
+ s.previousFailure = null;
362
+ }
363
+ s.wire = buildView(s);
364
+ return s;
365
+ }
366
+ function buildView(s) {
367
+ return {
368
+ version: s.version,
369
+ sessionId: s.sessionId,
370
+ seq: s.seq,
371
+ updatedAt: s.updatedAt,
372
+ totals: s.totals,
373
+ models: s.models,
374
+ turn: s.turn,
375
+ pending: s.open ? {
376
+ turn: s.open.turn,
377
+ step: s.open.step,
378
+ start: s.open.start,
379
+ reasoningFirst: s.open.reasoningFirst,
380
+ reasoningLast: s.open.reasoningLast,
381
+ lastContentAt: s.open.lastContentAt
382
+ } : null,
383
+ findings: s.findings,
384
+ findingCount: s.findingCount
385
+ };
386
+ }
387
+ function viewOf(s) {
388
+ return s.wire;
389
+ }
390
+ //#endregion
391
+ //#region src/insights/projection.ts
392
+ const number = z.number().finite().nonnegative();
393
+ const nullableTime = z.number().finite().nullable();
394
+ const stats = z.object({
395
+ calls: number,
396
+ reported: number,
397
+ exactTotals: number,
398
+ tokens: number,
399
+ input: number,
400
+ output: number,
401
+ cacheRead: number,
402
+ cacheWrite: number,
403
+ reasoning: number,
404
+ reasoningReports: number,
405
+ modelMs: number,
406
+ timedCalls: number,
407
+ firstMs: number,
408
+ firstSamples: number,
409
+ reasoningMs: number,
410
+ tools: number,
411
+ toolErrors: number,
412
+ toolMs: number,
413
+ bashMs: number.optional(),
414
+ retries: number
415
+ });
416
+ const route = z.object({
417
+ provider: z.string(),
418
+ model: z.string(),
419
+ effort: z.string().nullable().optional()
420
+ });
421
+ const finding = z.object({
422
+ id: z.string(),
423
+ kind: z.string(),
424
+ turn: number,
425
+ seqs: z.array(number),
426
+ steps: z.array(number),
427
+ title: z.string(),
428
+ detail: z.string()
429
+ });
430
+ const turn = z.object({
431
+ number,
432
+ startedAt: z.number().finite(),
433
+ endedAt: nullableTime,
434
+ steps: number,
435
+ stats,
436
+ route: route.optional()
437
+ }).nullable();
438
+ const pending = z.object({
439
+ turn: number,
440
+ step: number,
441
+ start: nullableTime,
442
+ reasoningFirst: nullableTime,
443
+ reasoningLast: nullableTime,
444
+ lastContentAt: nullableTime
445
+ }).nullable();
446
+ const viewSchema = z.object({
447
+ version: z.literal(1),
448
+ sessionId: z.string(),
449
+ seq: z.number().int().min(-1),
450
+ updatedAt: z.number().finite(),
451
+ totals: stats,
452
+ models: z.array(stats.extend({
453
+ provider: z.string(),
454
+ model: z.string(),
455
+ effort: z.string().optional()
456
+ })),
457
+ turn,
458
+ pending,
459
+ findings: z.array(finding).max(30),
460
+ findingCount: number
461
+ });
462
+ const usage = z.object({
463
+ input: number,
464
+ output: number,
465
+ cacheRead: number,
466
+ cacheWrite: number,
467
+ reasoning: number.nullable(),
468
+ tokens: number,
469
+ exact: z.boolean()
470
+ }).nullable();
471
+ const stateSchema = z.object({
472
+ version: z.literal(1),
473
+ sessionId: z.string(),
474
+ skip: number,
475
+ seq: z.number().int().min(-1),
476
+ updatedAt: z.number().finite(),
477
+ route,
478
+ totals: stats,
479
+ models: z.array(stats.extend({
480
+ provider: z.string(),
481
+ model: z.string()
482
+ })),
483
+ turn,
484
+ open: z.object({
485
+ turn: number,
486
+ step: number,
487
+ start: nullableTime,
488
+ first: nullableTime,
489
+ reasoningFirst: nullableTime,
490
+ reasoningLast: nullableTime,
491
+ lastContentAt: nullableTime,
492
+ usage,
493
+ route
494
+ }).nullable(),
495
+ tools: z.array(z.object({
496
+ id: z.string(),
497
+ name: z.string(),
498
+ start: z.number().finite(),
499
+ seq: number,
500
+ step: number,
501
+ route,
502
+ signature: z.string().nullable()
503
+ })),
504
+ previousFailure: z.object({
505
+ signature: z.string(),
506
+ resultHash: z.string(),
507
+ turn: number,
508
+ seqs: z.array(number),
509
+ steps: z.array(number)
510
+ }).nullable(),
511
+ findings: z.array(finding).max(30),
512
+ findingCount: number,
513
+ wire: viewSchema
514
+ });
515
+ function installProjection(ctx) {
516
+ ctx.sessionProjections.register({
517
+ key: "watcherInsights",
518
+ stateVersion: 1,
519
+ stateSchema,
520
+ init: (header, inherited) => stateSchema.parse(initialState(header, inherited)),
521
+ apply: (state, event) => reduceEvent(state, event),
522
+ wire: {
523
+ viewSchema,
524
+ view: (state) => viewOf(state)
525
+ }
526
+ });
527
+ ctx.inject(["sessions"], (c) => {
528
+ try {
529
+ for (const session of c.sessions.list?.() ?? []) try {
530
+ c.sessionProjections.snapshot(session);
531
+ } catch {}
532
+ } catch {}
533
+ });
534
+ }
535
+ //#endregion
536
+ //#region src/dsh-watcher.ts
537
+ const name = "dsh-watcher";
538
+ const inject = ["sessionProjections", "sessions"];
539
+ /** Host-owned replayable statistics; no new transport or model-facing writes. */
540
+ function apply(ctx) {
541
+ console.log("[my-plugins/dsh-watcher] loaded");
542
+ installProjection(ctx);
543
+ }
544
+ //#endregion
545
+ export { apply, inject, name };
package/package.json ADDED
@@ -0,0 +1,108 @@
1
+ {
2
+ "name": "dsh-watcher",
3
+ "version": "0.4.0-insights.1",
4
+ "description": "Read-only DSH session insights and local model usage statistics",
5
+ "type": "module",
6
+ "main": "lib/dsh-watcher.js",
7
+ "types": "lib/types/dsh-watcher.d.ts",
8
+ "scripts": {
9
+ "build": "tsc -p tsconfig.json && tsdown",
10
+ "typecheck": "tsc -p tsconfig.json --noEmit",
11
+ "test": "tsc -p tsconfig.json && node --test tests/*.test.mjs"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./lib/types/dsh-watcher.d.ts",
16
+ "default": "./lib/dsh-watcher.js"
17
+ },
18
+ "./client": {
19
+ "types": "./lib/types/client/index.d.ts",
20
+ "default": "./lib/client.js"
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "dsh": {
25
+ "client": {
26
+ "inject": [
27
+ "@deepseek-ai/dsh-client-ui-chat",
28
+ "@deepseek-ai/dsh-client-ui-conversation",
29
+ "@deepseek-ai/dsh-client-ui-primitives",
30
+ "@deepseek-ai/dsh-client-ui-renderer",
31
+ "@deepseek-ai/dsh-client-ui-session",
32
+ "@deepseek-ai/dsh-client-ui-settings",
33
+ "@deepseek-ai/dsh-api-remotes"
34
+ ],
35
+ "platform": "web"
36
+ },
37
+ "bundle": {
38
+ "patch": "./cordis.patch.yml"
39
+ }
40
+ },
41
+ "files": [
42
+ "lib/*.js",
43
+ "lib/*.js.map",
44
+ "lib/types/**/*.d.ts",
45
+ "cordis.yml",
46
+ "README.md",
47
+ "cordis.patch.yml"
48
+ ],
49
+ "engines": {
50
+ "node": "^22.19.0 || >=24.0.0"
51
+ },
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "git+https://github.com/aa2246740/dsh-watcher.git"
55
+ },
56
+ "homepage": "https://github.com/aa2246740/dsh-watcher#readme",
57
+ "bugs": {
58
+ "url": "https://github.com/aa2246740/dsh-watcher/issues"
59
+ },
60
+ "keywords": [
61
+ "deepseek-harness",
62
+ "dsh",
63
+ "dsh-plugin",
64
+ "watcher",
65
+ "agent",
66
+ "observability",
67
+ "workflow",
68
+ "plugin"
69
+ ],
70
+ "license": "MIT",
71
+ "peerDependencies": {
72
+ "@deepseek-ai/cordis": "^4.0.2",
73
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-rc.1",
74
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
75
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-rc.1",
76
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-rc.1",
77
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.2-rc.1",
78
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
79
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
80
+ "@deepseek-ai/dsh-session-stats": "^0.1.2-rc.1",
81
+ "@deepseek-ai/dsh-session-projection": "^0.1.2-rc.1",
82
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-rc.1",
83
+ "@deepseek-ai/dsh-api-remotes": "^0.1.2-rc.1"
84
+ },
85
+ "dependencies": {
86
+ "zod": "^4.4.3"
87
+ },
88
+ "devDependencies": {
89
+ "@deepseek-ai/cordis": "^4.0.2",
90
+ "@deepseek-ai/dsh-client-ui-chat": "0.1.2-rc.1",
91
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1",
92
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
93
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
94
+ "@deepseek-ai/dsh-client-ui-session": "0.1.2-rc.1",
95
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
96
+ "@deepseek-ai/dsh-session": "0.1.2-rc.1",
97
+ "@deepseek-ai/dsh-session-stats": "0.1.2-rc.1",
98
+ "@deepseek-ai/dsh-session-projection": "0.1.2-rc.1",
99
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.2-rc.1",
100
+ "@deepseek-ai/dsh-api-remotes": "0.1.2-rc.1",
101
+ "@types/react": "~18.3.1",
102
+ "@types/react-dom": "~18.3.0",
103
+ "react": "^18.2.0",
104
+ "react-dom": "^18.2.0",
105
+ "tsdown": "^0.22.2",
106
+ "typescript": "^6.0.3"
107
+ }
108
+ }