dsh-context 0.8.0 → 0.9.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.
Files changed (3) hide show
  1. package/lib/client.js +226 -439
  2. package/lib/index.js +269 -210
  3. package/package.json +9 -2
package/lib/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/host/timeline.ts
2
+ import { z } from "zod";
3
+
1
4
  // src/host/pricing.ts
2
5
  var CHARS_PER_TOKEN = 4;
3
6
  var BLOCK_OVERHEAD = 4;
@@ -64,6 +67,8 @@ function isInjection(source) {
64
67
  // src/host/fold.ts
65
68
  var MAX_REQUEST_STEPS = 1500;
66
69
  var MAX_KEPT_TURNS = 300;
70
+ var MAX_EVENTS = 400;
71
+ var MAX_NODES = 200;
67
72
  function trimToLastTurns(requests, maxTurns) {
68
73
  let runs = 0;
69
74
  let start = requests.length;
@@ -79,17 +84,33 @@ function trimToLastTurns(requests, maxTurns) {
79
84
  }
80
85
  return requests.slice(start);
81
86
  }
82
- function createFold() {
87
+ function countTurnRuns(requests) {
88
+ let runs = 0;
89
+ let prevTurn;
90
+ for (const r of requests) {
91
+ if (r.turn !== prevTurn) {
92
+ runs++;
93
+ prevTurn = r.turn;
94
+ }
95
+ }
96
+ return runs;
97
+ }
98
+ function trimState(st) {
99
+ if (countTurnRuns(st.requests) > MAX_KEPT_TURNS) {
100
+ st.requests = trimToLastTurns(st.requests, MAX_KEPT_TURNS);
101
+ }
102
+ if (st.requests.length > MAX_REQUEST_STEPS) {
103
+ st.requests = st.requests.slice(-MAX_REQUEST_STEPS);
104
+ }
105
+ if (st.events.length > MAX_EVENTS) st.events = st.events.slice(-MAX_EVENTS);
106
+ }
107
+ function createTimelineState() {
83
108
  return {
84
- n: 0,
85
- // number of log events already folded
86
109
  surface: [],
87
- // { seq, cat, tokens, form?, text?, tool?, err?, skill?, calls? }
88
110
  sums: { user: 0, inject: 0, assistant: 0, tool: 0 },
89
111
  systemTokens: 0,
90
112
  toolsTokens: 0,
91
113
  toolList: [],
92
- // { name, tokens }
93
114
  model: void 0,
94
115
  provider: void 0,
95
116
  lastModel: void 0,
@@ -98,11 +119,8 @@ function createFold() {
98
119
  sampledSurfaceTokens: void 0,
99
120
  occupancyWindow: void 0,
100
121
  requests: [],
101
- // one entry per answered model call
102
122
  events: [],
103
- // notable context events (structured; the Client labels them)
104
123
  callNames: {}
105
- // callId -> tool name
106
124
  };
107
125
  }
108
126
  function categoryOf(type, message) {
@@ -132,9 +150,12 @@ function applySurface(st, ev, type, data, message) {
132
150
  if (names.length > 0) node.calls = names.slice(0, 3);
133
151
  }
134
152
  } else if (type === "tool/result") {
153
+ const srcId = source?.callId;
154
+ const srcName = typeof srcId === "string" ? st.callNames[srcId] : void 0;
135
155
  const block = message?.content?.[0];
136
- const tname = block && block.callId !== void 0 ? st.callNames[block.callId] : void 0;
137
- if (tname) node.tool = tname;
156
+ const blockId = block?.toolCallId;
157
+ if (srcName) node.tool = srcName;
158
+ else if (typeof blockId === "string") node.tool = st.callNames[blockId];
138
159
  if (data?.error) node.err = true;
139
160
  } else if (source?.kind === "skill-invocation") {
140
161
  node.skill = typeof source.name === "string" ? source.name : "?";
@@ -196,170 +217,179 @@ function sampleUsage(st, usage) {
196
217
  st.pressureTokens = pressureTokens;
197
218
  st.sampledSurfaceTokens = st.sums.user + st.sums.inject + st.sums.assistant + st.sums.tool;
198
219
  }
199
- function foldInto(st, events) {
200
- for (let e = st.n; e < events.length; e++) {
201
- const ev = events[e];
202
- if (ev === null || typeof ev !== "object") continue;
203
- const data = ev.data;
204
- switch (ev.type) {
205
- case "request/header": {
206
- const header = data?.header ?? {};
207
- const tools = Array.isArray(header.tools) ? header.tools : [];
208
- st.toolList = tools.map((t) => ({
209
- name: typeof t.name === "string" ? t.name : "?",
210
- tokens: estimateToolSchema(t)
211
- }));
212
- st.toolsTokens = estimateToolsTotal(tools);
213
- st.systemTokens = estimateSystem(header.system);
214
- if (header.config && typeof header.config.model === "string") st.model = header.config.model;
215
- if (header.config && typeof header.config.provider === "string") st.provider = header.config.provider;
216
- if (data?.reason === "change" && st.model && st.lastModel && st.model !== st.lastModel) {
217
- st.events.push({ seq: ev.seq, time: ev.time, kind: "model", from: st.lastModel, to: st.model });
218
- }
219
- if (st.model) st.lastModel = st.model;
220
- break;
220
+ function applyTimeline(state, event) {
221
+ let st;
222
+ const ensure = () => st ??= {
223
+ ...state,
224
+ surface: [...state.surface],
225
+ sums: { ...state.sums },
226
+ toolList: [...state.toolList],
227
+ requests: [...state.requests],
228
+ events: [...state.events],
229
+ callNames: { ...state.callNames }
230
+ };
231
+ const data = event.data;
232
+ switch (event.type) {
233
+ case "request/header": {
234
+ const header = data?.header ?? {};
235
+ const tools = Array.isArray(header.tools) ? header.tools : [];
236
+ const s = ensure();
237
+ s.toolList = tools.map((t) => ({
238
+ name: typeof t.name === "string" ? t.name : "?",
239
+ tokens: estimateToolSchema(t)
240
+ }));
241
+ s.toolsTokens = estimateToolsTotal(tools);
242
+ s.systemTokens = estimateSystem(header.system);
243
+ if (header.config && typeof header.config.model === "string") s.model = header.config.model;
244
+ if (header.config && typeof header.config.provider === "string") s.provider = header.config.provider;
245
+ if (data?.reason === "change" && s.model && s.lastModel && s.model !== s.lastModel) {
246
+ s.events.push({ seq: event.seq, time: event.time, kind: "model", from: s.lastModel, to: s.model });
221
247
  }
222
- case "request/context":
223
- if (data && typeof data.contextWindow === "number") st.contextWindow = data.contextWindow;
224
- if (data && typeof data.model === "string") st.model = data.model;
225
- if (data && typeof data.provider === "string") st.provider = data.provider;
226
- st.occupancyWindow = data && typeof data.contextWindow === "number" ? data.contextWindow : void 0;
227
- break;
228
- case "tool/call":
229
- if (data && data.callId !== void 0 && typeof data.name === "string") st.callNames[String(data.callId)] = data.name;
230
- break;
231
- case "user/message": {
232
- const msg = data;
233
- const node = applySurface(st, ev, ev.type, data, msg);
234
- const source = msg?.source;
235
- if (isInjection(source)) {
236
- const rec = {
237
- seq: ev.seq,
238
- time: ev.time,
239
- kind: "inject",
240
- form: source.form || "context",
241
- tokens: node.tokens
242
- };
243
- if (source.kind === "skill-invocation") {
244
- rec.sub = "skill";
245
- rec.name = typeof source.name === "string" ? source.name : "?";
246
- } else if (typeof source.plugin === "string" && source.plugin !== "") {
247
- rec.name = source.plugin;
248
- }
249
- st.events.push(rec);
248
+ if (s.model) s.lastModel = s.model;
249
+ break;
250
+ }
251
+ case "request/context": {
252
+ const s = ensure();
253
+ if (data && typeof data.contextWindow === "number") s.contextWindow = data.contextWindow;
254
+ if (data && typeof data.model === "string") s.model = data.model;
255
+ if (data && typeof data.provider === "string") s.provider = data.provider;
256
+ s.occupancyWindow = data && typeof data.contextWindow === "number" ? data.contextWindow : void 0;
257
+ break;
258
+ }
259
+ case "tool/call": {
260
+ if (data && data.callId !== void 0 && typeof data.name === "string") {
261
+ const s = ensure();
262
+ s.callNames[String(data.callId)] = data.name;
263
+ }
264
+ break;
265
+ }
266
+ case "user/message": {
267
+ const msg = data;
268
+ const s = ensure();
269
+ const node = applySurface(s, event, event.type, data, msg);
270
+ const source = msg?.source;
271
+ if (isInjection(source)) {
272
+ const rec = {
273
+ seq: event.seq,
274
+ time: event.time,
275
+ kind: "inject",
276
+ form: source.form || "context",
277
+ tokens: node.tokens
278
+ };
279
+ if (source.kind === "skill-invocation") {
280
+ rec.sub = "skill";
281
+ rec.name = typeof source.name === "string" ? source.name : "?";
282
+ } else if (typeof source.plugin === "string" && source.plugin !== "") {
283
+ rec.name = source.plugin;
250
284
  }
251
- break;
285
+ s.events.push(rec);
252
286
  }
253
- case "tool/result": {
254
- const toolMsg = data?.message ?? null;
255
- applySurface(st, ev, ev.type, data, toolMsg);
256
- break;
287
+ break;
288
+ }
289
+ case "tool/result": {
290
+ const toolMsg = data?.message ?? null;
291
+ const s = ensure();
292
+ applySurface(s, event, event.type, data, toolMsg);
293
+ break;
294
+ }
295
+ case "assistant/chunk": {
296
+ const chunk = data?.chunk;
297
+ if (chunk !== void 0 && chunk.type === "usage") {
298
+ const s = ensure();
299
+ sampleUsage(s, chunk.usage);
257
300
  }
258
- case "assistant/chunk": {
259
- const chunk = data?.chunk;
260
- if (chunk !== void 0 && chunk.type === "usage") sampleUsage(st, chunk.usage);
261
- break;
301
+ break;
302
+ }
303
+ case "assistant/message": {
304
+ const usage = data?.usage;
305
+ const s = ensure();
306
+ sampleUsage(s, usage);
307
+ const total = s.systemTokens + s.toolsTokens + s.sums.user + s.sums.inject + s.sums.assistant + s.sums.tool;
308
+ const record = {
309
+ turn: data && typeof data.turn === "number" ? data.turn : void 0,
310
+ step: data && typeof data.step === "number" ? data.step : void 0,
311
+ time: event.time,
312
+ seq: event.seq,
313
+ system: s.systemTokens,
314
+ tools: s.toolsTokens,
315
+ user: s.sums.user,
316
+ inject: s.sums.inject,
317
+ assistant: s.sums.assistant,
318
+ tool: s.sums.tool,
319
+ total
320
+ };
321
+ if (usage && typeof usage.inputTokens === "number") {
322
+ record.prompt = usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
323
+ if (typeof usage.outputTokens === "number") record.output = usage.outputTokens;
262
324
  }
263
- case "assistant/message": {
264
- const usage = data?.usage;
265
- sampleUsage(st, usage);
266
- const total = st.systemTokens + st.toolsTokens + st.sums.user + st.sums.inject + st.sums.assistant + st.sums.tool;
267
- const record = {
268
- turn: data && typeof data.turn === "number" ? data.turn : void 0,
269
- step: data && typeof data.step === "number" ? data.step : void 0,
270
- time: ev.time,
271
- seq: ev.seq,
272
- system: st.systemTokens,
273
- tools: st.toolsTokens,
274
- user: st.sums.user,
275
- inject: st.sums.inject,
276
- assistant: st.sums.assistant,
277
- tool: st.sums.tool,
278
- total
279
- };
280
- if (usage && typeof usage.inputTokens === "number") {
281
- record.prompt = usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
282
- if (typeof usage.outputTokens === "number") record.output = usage.outputTokens;
283
- }
284
- st.requests.push(record);
285
- const asstMsg = data?.message ?? null;
286
- applySurface(st, ev, ev.type, data, asstMsg);
287
- break;
325
+ s.requests.push(record);
326
+ const asstMsg = data?.message ?? null;
327
+ applySurface(s, event, event.type, data, asstMsg);
328
+ break;
329
+ }
330
+ case "compaction/summary":
331
+ case "compaction/prune": {
332
+ const s = ensure();
333
+ if (data && Array.isArray(data.shadowedSeqs)) {
334
+ s.pendingShadowedSeqs = data.shadowedSeqs.filter((x) => typeof x === "number");
288
335
  }
289
- case "compaction/summary":
290
- if (data && Array.isArray(data.shadowedSeqs)) {
291
- st.pendingShadowedSeqs = data.shadowedSeqs.filter((s) => typeof s === "number");
292
- }
293
- st.events.push({
294
- seq: ev.seq,
295
- time: ev.time,
296
- kind: "compaction",
297
- tokens: data && typeof data.shadowedTokenCount === "number" ? data.shadowedTokenCount : 0,
298
- count: data && Array.isArray(data.shadowedSeqs) ? data.shadowedSeqs.length : 0
299
- });
300
- break;
301
- case "compaction/prune":
302
- if (data && Array.isArray(data.shadowedSeqs)) {
303
- st.pendingShadowedSeqs = data.shadowedSeqs.filter((s) => typeof s === "number");
304
- }
305
- st.events.push({
306
- seq: ev.seq,
307
- time: ev.time,
308
- kind: "prune",
309
- tokens: data && typeof data.shadowedTokenCount === "number" ? data.shadowedTokenCount : 0
310
- });
311
- break;
312
- default:
313
- break;
336
+ s.events.push({
337
+ seq: event.seq,
338
+ time: event.time,
339
+ kind: event.type === "compaction/summary" ? "compaction" : "prune",
340
+ tokens: data && typeof data.shadowedTokenCount === "number" ? data.shadowedTokenCount : 0,
341
+ ...event.type === "compaction/summary" && data && Array.isArray(data.shadowedSeqs) ? { count: data.shadowedSeqs.length } : {}
342
+ });
343
+ break;
314
344
  }
345
+ default:
346
+ return state;
315
347
  }
316
- st.n = events.length;
317
- if (st.requests.length > MAX_REQUEST_STEPS) {
318
- st.requests = trimToLastTurns(st.requests, MAX_KEPT_TURNS);
319
- if (st.requests.length > MAX_REQUEST_STEPS) st.requests = st.requests.slice(-MAX_REQUEST_STEPS);
348
+ if (st !== void 0) {
349
+ trimState(st);
350
+ return st;
320
351
  }
321
- if (st.events.length > 400) st.events = st.events.slice(-400);
352
+ return state;
322
353
  }
323
-
324
- // src/host/snapshot.ts
325
- function buildResult(st) {
326
- const surfaceTotal = st.sums.user + st.sums.inject + st.sums.assistant + st.sums.tool;
327
- const projectedTokens = st.pressureTokens !== void 0 && st.sampledSurfaceTokens !== void 0 ? Math.max(0, st.pressureTokens + surfaceTotal - st.sampledSurfaceTokens) : void 0;
354
+ function buildTimelineView(state) {
355
+ const surfaceTotal = state.sums.user + state.sums.inject + state.sums.assistant + state.sums.tool;
356
+ const projectedTokens = state.pressureTokens !== void 0 && state.sampledSurfaceTokens !== void 0 ? Math.max(0, state.pressureTokens + surfaceTotal - state.sampledSurfaceTokens) : void 0;
328
357
  const result = {
329
358
  ok: true,
330
- model: st.model,
331
- provider: st.provider,
332
- contextWindow: st.contextWindow,
359
+ model: state.model,
360
+ provider: state.provider,
361
+ contextWindow: state.contextWindow,
333
362
  current: {
334
- system: st.systemTokens,
335
- tools: st.toolsTokens,
336
- user: st.sums.user,
337
- inject: st.sums.inject,
338
- assistant: st.sums.assistant,
339
- tool: st.sums.tool,
340
- total: surfaceTotal + st.systemTokens + st.toolsTokens
363
+ system: state.systemTokens,
364
+ tools: state.toolsTokens,
365
+ user: state.sums.user,
366
+ inject: state.sums.inject,
367
+ assistant: state.sums.assistant,
368
+ tool: state.sums.tool,
369
+ total: surfaceTotal + state.systemTokens + state.toolsTokens
341
370
  },
342
371
  occupancy: {
343
- ...st.pressureTokens === void 0 ? {} : { pressureTokens: st.pressureTokens },
372
+ ...state.pressureTokens === void 0 ? {} : { pressureTokens: state.pressureTokens },
344
373
  surfaceTokens: surfaceTotal,
345
- ...st.sampledSurfaceTokens === void 0 ? {} : { sampledSurfaceTokens: st.sampledSurfaceTokens },
374
+ ...state.sampledSurfaceTokens === void 0 ? {} : { sampledSurfaceTokens: state.sampledSurfaceTokens },
346
375
  ...projectedTokens === void 0 ? {} : { projectedTokens },
347
- ...st.occupancyWindow === void 0 ? {} : { contextWindow: st.occupancyWindow }
376
+ ...state.occupancyWindow === void 0 ? {} : { contextWindow: state.occupancyWindow }
348
377
  },
349
- toolList: st.toolList,
350
- requests: st.requests,
351
- events: st.events,
378
+ toolList: state.toolList,
379
+ requests: state.requests.map((r) => ({ ...r })),
380
+ events: state.events.map((e) => ({ ...e })),
352
381
  nodes: [],
353
382
  droppedNodes: 0
354
383
  };
355
- const MAX_NODES = 200;
356
- result.droppedNodes = Math.max(0, st.surface.length - MAX_NODES);
357
- result.nodes = st.surface.slice(-MAX_NODES);
384
+ result.droppedNodes = Math.max(0, state.surface.length - MAX_NODES);
385
+ result.nodes = state.surface.slice(-MAX_NODES);
386
+ const requests = result.requests;
387
+ const events = result.events;
358
388
  let ri = 0;
359
- for (const ev of result.events) {
360
- while (ri < result.requests.length && result.requests[ri].seq <= ev.seq) ri++;
361
- const next = result.requests[ri];
362
- const prev = ri > 0 ? result.requests[ri - 1] : void 0;
389
+ for (const ev of events) {
390
+ while (ri < requests.length && requests[ri].seq <= ev.seq) ri++;
391
+ const next = requests[ri];
392
+ const prev = ri > 0 ? requests[ri - 1] : void 0;
363
393
  if (next !== void 0 && typeof next.turn === "number" && typeof next.step === "number") {
364
394
  ev.turn = next.turn;
365
395
  ev.step = next.step;
@@ -371,68 +401,97 @@ function buildResult(st) {
371
401
  }
372
402
  return result;
373
403
  }
374
- async function computeSnapshot(ctx, states, sessionId) {
375
- let st = states.get(sessionId);
376
- if (st === void 0) {
377
- st = { fold: createFold(), count: -1, result: null };
378
- states.set(sessionId, st);
379
- }
380
- const sessions = ctx.get("sessions");
381
- const sessionQuery = ctx.get("sessionQuery");
382
- const live = sessions !== void 0 ? sessions.get(sessionId) : void 0;
383
- let events;
384
- if (live !== void 0) {
385
- events = live.events;
386
- } else {
387
- if (sessionQuery === void 0) throw new Error("session is not live and sessionQuery is unavailable");
388
- if (st.result !== null && st.count >= 0) {
389
- const records = await sessionQuery.listEvents(sessionId);
390
- if (records.length === st.count) return st.result;
391
- }
392
- const snapshot = await sessionQuery.readSession(sessionId);
393
- events = snapshot && Array.isArray(snapshot.events) ? snapshot.events : [];
394
- }
395
- if (events.length === st.count && st.result !== null) return st.result;
396
- if (events.length < st.fold.n) st.fold = createFold();
397
- foldInto(st.fold, events);
398
- st.count = events.length;
399
- st.result = buildResult(st.fold);
400
- return st.result;
401
- }
404
+
405
+ // src/host/timeline.ts
406
+ var surfaceNodeSchema = z.object({
407
+ seq: z.number().int().nonnegative(),
408
+ time: z.number().optional(),
409
+ cat: z.enum(["user", "inject", "assistant", "tool"]),
410
+ tokens: z.number().int().nonnegative(),
411
+ form: z.string().optional(),
412
+ text: z.string().optional(),
413
+ tool: z.string().optional(),
414
+ err: z.boolean().optional(),
415
+ skill: z.string().optional(),
416
+ calls: z.array(z.string()).optional()
417
+ }).strict();
418
+ var requestRecordSchema = z.object({
419
+ turn: z.number().optional(),
420
+ step: z.number().optional(),
421
+ time: z.number(),
422
+ seq: z.number(),
423
+ system: z.number().int().nonnegative(),
424
+ tools: z.number().int().nonnegative(),
425
+ user: z.number().int().nonnegative(),
426
+ inject: z.number().int().nonnegative(),
427
+ assistant: z.number().int().nonnegative(),
428
+ tool: z.number().int().nonnegative(),
429
+ total: z.number().int().nonnegative(),
430
+ prompt: z.number().int().nonnegative().optional(),
431
+ output: z.number().int().nonnegative().optional(),
432
+ stepCount: z.number().int().positive().optional()
433
+ }).strict();
434
+ var contextEventSchema = z.object({
435
+ seq: z.number(),
436
+ time: z.number(),
437
+ kind: z.enum(["compaction", "prune", "inject", "model"]),
438
+ form: z.string().optional(),
439
+ tokens: z.number().optional(),
440
+ count: z.number().optional(),
441
+ sub: z.string().optional(),
442
+ name: z.string().optional(),
443
+ from: z.string().optional(),
444
+ to: z.string().optional(),
445
+ fromTurn: z.number().optional(),
446
+ fromStep: z.number().optional(),
447
+ turn: z.number().optional(),
448
+ step: z.number().optional()
449
+ }).strict();
450
+ var currentSchema = z.object({
451
+ system: z.number().int().nonnegative(),
452
+ tools: z.number().int().nonnegative(),
453
+ user: z.number().int().nonnegative(),
454
+ inject: z.number().int().nonnegative(),
455
+ assistant: z.number().int().nonnegative(),
456
+ tool: z.number().int().nonnegative(),
457
+ total: z.number().int().nonnegative()
458
+ }).strict();
459
+ var occupancySchema = z.object({
460
+ pressureTokens: z.number().int().nonnegative().optional(),
461
+ surfaceTokens: z.number().int().nonnegative(),
462
+ sampledSurfaceTokens: z.number().int().nonnegative().optional(),
463
+ projectedTokens: z.number().int().nonnegative().optional(),
464
+ contextWindow: z.number().int().positive().optional()
465
+ }).strict();
466
+ var contextTimelineSchema = z.object({
467
+ ok: z.literal(true),
468
+ model: z.string().optional(),
469
+ provider: z.string().optional(),
470
+ contextWindow: z.number().optional(),
471
+ current: currentSchema,
472
+ occupancy: occupancySchema.optional(),
473
+ toolList: z.array(z.object({ name: z.string(), tokens: z.number().int().nonnegative() }).strict()),
474
+ requests: z.array(requestRecordSchema),
475
+ events: z.array(contextEventSchema),
476
+ nodes: z.array(surfaceNodeSchema),
477
+ droppedNodes: z.number().int().nonnegative()
478
+ }).strict();
479
+ var contextTimelineDefinition = {
480
+ key: "contextTimeline",
481
+ schema: contextTimelineSchema,
482
+ init: () => createTimelineState(),
483
+ apply: (state, event) => applyTimeline(state, event),
484
+ view: (state) => buildTimelineView(state),
485
+ stateVersion: 1
486
+ };
402
487
 
403
488
  // src/host/index.ts
404
489
  var name = "dsh-context";
405
- var inject = ["connection"];
490
+ var inject = ["sessionProjections"];
406
491
  function apply(ctx) {
407
- const states = /* @__PURE__ */ new Map();
408
- ctx.effect(() => {
409
- return ctx.connection.rpc.handle(
410
- "/dsh-context",
411
- async (endpoint, payload) => {
412
- try {
413
- if (endpoint !== "snapshot") {
414
- return { ok: false, error: { code: "internal", message: `unknown endpoint: ${endpoint}`, details: {} } };
415
- }
416
- const sessionId = payload !== null && typeof payload === "object" ? payload.sessionId : void 0;
417
- if (typeof sessionId !== "string" || sessionId === "") {
418
- return { ok: false, error: { code: "internal", message: "missing sessionId", details: {} } };
419
- }
420
- const value = await computeSnapshot(ctx, states, sessionId);
421
- return { ok: true, value };
422
- } catch (err) {
423
- return {
424
- ok: false,
425
- error: {
426
- code: "internal",
427
- message: err instanceof Error ? err.message : String(err),
428
- details: {}
429
- }
430
- };
431
- }
432
- },
433
- { authority: "trusted-host" }
434
- );
435
- }, "dsh-context: rpc channel");
492
+ ctx.inject(["sessionProjections"], (projectionCtx) => {
493
+ projectionCtx.sessionProjections.register(contextTimelineDefinition);
494
+ });
436
495
  }
437
496
  export {
438
497
  apply,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.8.0",
4
- "description": "Context insight panel for DeepSeek Harness: see what the model's context window is made of and how it evolves — composition, per-request history, compactions, injections, and model switches.",
3
+ "version": "0.9.0",
4
+ "description": "Context insight panel for DeepSeek Harness: see what the model's context window is made of and how it evolves — composition, per-request history, compactions, injections, and model switches. Streamed through the harness's session-projection pipeline.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {
7
7
  "type": "git",
@@ -56,8 +56,15 @@
56
56
  "ui"
57
57
  ],
58
58
  "license": "Apache-2.0",
59
+ "dependencies": {
60
+ "zod": "^4.4.3"
61
+ },
59
62
  "devDependencies": {
60
63
  "@deepseek-ai/cordis": "^4.0.1",
64
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.6",
65
+ "@deepseek-ai/dsh-session": "0.1.0-rc.6",
66
+ "@deepseek-ai/dsh-session-projection": "0.1.0-rc.6",
67
+ "@deepseek-ai/dsh-token-meter": "0.1.0-rc.6",
61
68
  "@types/react": "^18.3.31",
62
69
  "esbuild": "^0.28.2",
63
70
  "husky": "^9.1.7",