dsh-context 0.7.3 → 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.
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,27 +84,43 @@ 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,
96
117
  contextWindow: void 0,
118
+ pressureTokens: void 0,
119
+ sampledSurfaceTokens: void 0,
120
+ occupancyWindow: void 0,
97
121
  requests: [],
98
- // one entry per answered model call
99
122
  events: [],
100
- // notable context events (structured; the Client labels them)
101
123
  callNames: {}
102
- // callId -> tool name
103
124
  };
104
125
  }
105
126
  function categoryOf(type, message) {
@@ -129,9 +150,12 @@ function applySurface(st, ev, type, data, message) {
129
150
  if (names.length > 0) node.calls = names.slice(0, 3);
130
151
  }
131
152
  } else if (type === "tool/result") {
153
+ const srcId = source?.callId;
154
+ const srcName = typeof srcId === "string" ? st.callNames[srcId] : void 0;
132
155
  const block = message?.content?.[0];
133
- const tname = block && block.callId !== void 0 ? st.callNames[block.callId] : void 0;
134
- 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];
135
159
  if (data?.error) node.err = true;
136
160
  } else if (source?.kind === "skill-invocation") {
137
161
  node.skill = typeof source.name === "string" ? source.name : "?";
@@ -147,8 +171,22 @@ function applySurface(st, ev, type, data, message) {
147
171
  const utext = firstText(message?.content);
148
172
  if (utext !== "") node.text = utext;
149
173
  }
174
+ const shadowedSeqs = st.pendingShadowedSeqs;
175
+ st.pendingShadowedSeqs = void 0;
150
176
  const op = ev.surfaceOp;
151
177
  if (op !== null && typeof op === "object" && op.op === "replace") {
178
+ if (Array.isArray(shadowedSeqs) && shadowedSeqs.length > 0) {
179
+ const shadowed = new Set(shadowedSeqs);
180
+ const kept = [];
181
+ for (const n of st.surface) {
182
+ if (shadowed.has(n.seq)) st.sums[n.cat] -= n.tokens;
183
+ else kept.push(n);
184
+ }
185
+ st.surface = kept;
186
+ st.sums[cat] += node.tokens;
187
+ st.surface.push(node);
188
+ return node;
189
+ }
152
190
  let si = -1;
153
191
  let ei = -1;
154
192
  for (let i = 0; i < st.surface.length; i++) {
@@ -169,149 +207,189 @@ function applySurface(st, ev, type, data, message) {
169
207
  st.sums[cat] += node.tokens;
170
208
  return node;
171
209
  }
172
- function foldInto(st, events) {
173
- for (let e = st.n; e < events.length; e++) {
174
- const ev = events[e];
175
- if (ev === null || typeof ev !== "object") continue;
176
- const data = ev.data;
177
- switch (ev.type) {
178
- case "request/header": {
179
- const header = data?.header ?? {};
180
- const tools = Array.isArray(header.tools) ? header.tools : [];
181
- st.toolList = tools.map((t) => ({
182
- name: typeof t.name === "string" ? t.name : "?",
183
- tokens: estimateToolSchema(t)
184
- }));
185
- st.toolsTokens = estimateToolsTotal(tools);
186
- st.systemTokens = estimateSystem(header.system);
187
- if (header.config && typeof header.config.model === "string") st.model = header.config.model;
188
- if (header.config && typeof header.config.provider === "string") st.provider = header.config.provider;
189
- if (data?.reason === "change" && st.model && st.lastModel && st.model !== st.lastModel) {
190
- st.events.push({ seq: ev.seq, time: ev.time, kind: "model", from: st.lastModel, to: st.model });
191
- }
192
- if (st.model) st.lastModel = st.model;
193
- break;
194
- }
195
- case "request/context":
196
- if (data && typeof data.contextWindow === "number") st.contextWindow = data.contextWindow;
197
- if (data && typeof data.model === "string") st.model = data.model;
198
- if (data && typeof data.provider === "string") st.provider = data.provider;
199
- break;
200
- case "tool/call":
201
- if (data && data.callId !== void 0 && typeof data.name === "string") st.callNames[String(data.callId)] = data.name;
202
- break;
203
- case "user/message": {
204
- const msg = data;
205
- const node = applySurface(st, ev, ev.type, data, msg);
206
- const source = msg?.source;
207
- if (isInjection(source)) {
208
- const rec = {
209
- seq: ev.seq,
210
- time: ev.time,
211
- kind: "inject",
212
- form: source.form || "context",
213
- tokens: node.tokens
214
- };
215
- if (source.kind === "skill-invocation") {
216
- rec.sub = "skill";
217
- rec.name = typeof source.name === "string" ? source.name : "?";
218
- } else if (typeof source.plugin === "string" && source.plugin !== "") {
219
- rec.name = source.plugin;
220
- }
221
- st.events.push(rec);
222
- }
223
- break;
210
+ function pressureOf(usage) {
211
+ if (usage === void 0 || typeof usage.inputTokens !== "number") return void 0;
212
+ return usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
213
+ }
214
+ function sampleUsage(st, usage) {
215
+ const pressureTokens = pressureOf(usage);
216
+ if (pressureTokens === void 0) return;
217
+ st.pressureTokens = pressureTokens;
218
+ st.sampledSurfaceTokens = st.sums.user + st.sums.inject + st.sums.assistant + st.sums.tool;
219
+ }
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 });
224
247
  }
225
- case "tool/result": {
226
- const toolMsg = data?.message ?? null;
227
- applySurface(st, ev, ev.type, data, toolMsg);
228
- break;
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;
229
263
  }
230
- case "assistant/message": {
231
- const usage = data?.usage;
232
- const total = st.systemTokens + st.toolsTokens + st.sums.user + st.sums.inject + st.sums.assistant + st.sums.tool;
233
- const record = {
234
- turn: data && typeof data.turn === "number" ? data.turn : void 0,
235
- step: data && typeof data.step === "number" ? data.step : void 0,
236
- time: ev.time,
237
- seq: ev.seq,
238
- system: st.systemTokens,
239
- tools: st.toolsTokens,
240
- user: st.sums.user,
241
- inject: st.sums.inject,
242
- assistant: st.sums.assistant,
243
- tool: st.sums.tool,
244
- total
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
245
278
  };
246
- if (usage && typeof usage.inputTokens === "number") {
247
- record.prompt = usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
248
- if (typeof usage.outputTokens === "number") record.output = usage.outputTokens;
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;
249
284
  }
250
- st.requests.push(record);
251
- const asstMsg = data?.message ?? null;
252
- applySurface(st, ev, ev.type, data, asstMsg);
253
- break;
285
+ s.events.push(rec);
254
286
  }
255
- case "compaction/summary":
256
- st.events.push({
257
- seq: ev.seq,
258
- time: ev.time,
259
- kind: "compaction",
260
- tokens: data && typeof data.shadowedTokenCount === "number" ? data.shadowedTokenCount : 0,
261
- count: data && Array.isArray(data.shadowedSeqs) ? data.shadowedSeqs.length : 0
262
- });
263
- break;
264
- case "compaction/prune":
265
- st.events.push({
266
- seq: ev.seq,
267
- time: ev.time,
268
- kind: "prune",
269
- tokens: data && typeof data.shadowedTokenCount === "number" ? data.shadowedTokenCount : 0
270
- });
271
- break;
272
- default:
273
- 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);
300
+ }
301
+ break;
274
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;
324
+ }
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");
335
+ }
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;
344
+ }
345
+ default:
346
+ return state;
275
347
  }
276
- st.n = events.length;
277
- if (st.requests.length > MAX_REQUEST_STEPS) {
278
- st.requests = trimToLastTurns(st.requests, MAX_KEPT_TURNS);
279
- 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;
280
351
  }
281
- if (st.events.length > 400) st.events = st.events.slice(-400);
352
+ return state;
282
353
  }
283
-
284
- // src/host/snapshot.ts
285
- function buildResult(st) {
286
- const surfaceTotal = st.sums.user + st.sums.inject + st.sums.assistant + st.sums.tool;
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;
287
357
  const result = {
288
358
  ok: true,
289
- model: st.model,
290
- provider: st.provider,
291
- contextWindow: st.contextWindow,
359
+ model: state.model,
360
+ provider: state.provider,
361
+ contextWindow: state.contextWindow,
292
362
  current: {
293
- system: st.systemTokens,
294
- tools: st.toolsTokens,
295
- user: st.sums.user,
296
- inject: st.sums.inject,
297
- assistant: st.sums.assistant,
298
- tool: st.sums.tool,
299
- 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
300
370
  },
301
- toolList: st.toolList,
302
- requests: st.requests,
303
- events: st.events,
371
+ occupancy: {
372
+ ...state.pressureTokens === void 0 ? {} : { pressureTokens: state.pressureTokens },
373
+ surfaceTokens: surfaceTotal,
374
+ ...state.sampledSurfaceTokens === void 0 ? {} : { sampledSurfaceTokens: state.sampledSurfaceTokens },
375
+ ...projectedTokens === void 0 ? {} : { projectedTokens },
376
+ ...state.occupancyWindow === void 0 ? {} : { contextWindow: state.occupancyWindow }
377
+ },
378
+ toolList: state.toolList,
379
+ requests: state.requests.map((r) => ({ ...r })),
380
+ events: state.events.map((e) => ({ ...e })),
304
381
  nodes: [],
305
382
  droppedNodes: 0
306
383
  };
307
- const MAX_NODES = 200;
308
- result.droppedNodes = Math.max(0, st.surface.length - MAX_NODES);
309
- 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;
310
388
  let ri = 0;
311
- for (const ev of result.events) {
312
- while (ri < result.requests.length && result.requests[ri].seq <= ev.seq) ri++;
313
- const next = result.requests[ri];
314
- 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;
315
393
  if (next !== void 0 && typeof next.turn === "number" && typeof next.step === "number") {
316
394
  ev.turn = next.turn;
317
395
  ev.step = next.step;
@@ -323,68 +401,97 @@ function buildResult(st) {
323
401
  }
324
402
  return result;
325
403
  }
326
- async function computeSnapshot(ctx, states, sessionId) {
327
- let st = states.get(sessionId);
328
- if (st === void 0) {
329
- st = { fold: createFold(), count: -1, result: null };
330
- states.set(sessionId, st);
331
- }
332
- const sessions = ctx.get("sessions");
333
- const sessionQuery = ctx.get("sessionQuery");
334
- const live = sessions !== void 0 ? sessions.get(sessionId) : void 0;
335
- let events;
336
- if (live !== void 0) {
337
- events = live.events;
338
- } else {
339
- if (sessionQuery === void 0) throw new Error("session is not live and sessionQuery is unavailable");
340
- if (st.result !== null && st.count >= 0) {
341
- const records = await sessionQuery.listEvents(sessionId);
342
- if (records.length === st.count) return st.result;
343
- }
344
- const snapshot = await sessionQuery.readSession(sessionId);
345
- events = snapshot && Array.isArray(snapshot.events) ? snapshot.events : [];
346
- }
347
- if (events.length === st.count && st.result !== null) return st.result;
348
- if (events.length < st.fold.n) st.fold = createFold();
349
- foldInto(st.fold, events);
350
- st.count = events.length;
351
- st.result = buildResult(st.fold);
352
- return st.result;
353
- }
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
+ };
354
487
 
355
488
  // src/host/index.ts
356
489
  var name = "dsh-context";
357
- var inject = ["connection"];
490
+ var inject = ["sessionProjections"];
358
491
  function apply(ctx) {
359
- const states = /* @__PURE__ */ new Map();
360
- ctx.effect(() => {
361
- return ctx.connection.rpc.handle(
362
- "/dsh-context",
363
- async (endpoint, payload) => {
364
- try {
365
- if (endpoint !== "snapshot") {
366
- return { ok: false, error: { code: "internal", message: `unknown endpoint: ${endpoint}`, details: {} } };
367
- }
368
- const sessionId = payload !== null && typeof payload === "object" ? payload.sessionId : void 0;
369
- if (typeof sessionId !== "string" || sessionId === "") {
370
- return { ok: false, error: { code: "internal", message: "missing sessionId", details: {} } };
371
- }
372
- const value = await computeSnapshot(ctx, states, sessionId);
373
- return { ok: true, value };
374
- } catch (err) {
375
- return {
376
- ok: false,
377
- error: {
378
- code: "internal",
379
- message: err instanceof Error ? err.message : String(err),
380
- details: {}
381
- }
382
- };
383
- }
384
- },
385
- { authority: "trusted-host" }
386
- );
387
- }, "dsh-context: rpc channel");
492
+ ctx.inject(["sessionProjections"], (projectionCtx) => {
493
+ projectionCtx.sessionProjections.register(contextTimelineDefinition);
494
+ });
388
495
  }
389
496
  export {
390
497
  apply,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.7.3",
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",