dsh-context 0.51.1 → 0.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -269,7 +269,13 @@ declare module '@deepseek-ai/dsh-session-projection/types' {
269
269
  contextHeaders: HeadersState;
270
270
  }
271
271
  }
272
- type Category = 'user' | 'inject' | 'assistant' | 'tool';
272
+ /**
273
+ * The priced surface buckets. `skill` carries every skill-machinery content
274
+ * the harness injects (issue #66): the `<available_skills>` catalog digest,
275
+ * a user-explicit `/name` invocation's instructions message, and the content
276
+ * a `skill`-tool load returns (modeled as a tool result by the harness).
277
+ */
278
+ type Category = 'user' | 'inject' | 'skill' | 'assistant' | 'tool';
273
279
  /**
274
280
  * One live system-prompt node (Snapshot.systems) — the harness models the
275
281
  * system prompt as a surface node, so its TEXT is fetched on demand from the
@@ -329,6 +335,7 @@ interface Snapshot {
329
335
  tools: number;
330
336
  user: number;
331
337
  inject: number;
338
+ skill: number;
332
339
  assistant: number;
333
340
  tool: number;
334
341
  total: number;
@@ -613,6 +620,14 @@ interface SurfaceNode {
613
620
  */
614
621
  gone?: number;
615
622
  form?: string;
623
+ /**
624
+ * The producer identity the matching inject event names (host pricing.ts
625
+ * `injectionSourceName`: the plugin id, the reconciled instruction files,
626
+ * or the durable kind). Stamped on injection nodes alongside the event, so
627
+ * the browser rows label them the way the events card does; absent when the
628
+ * source carries no readable identity or the node predates the stamp.
629
+ */
630
+ name?: string;
616
631
  text?: string;
617
632
  tool?: string;
618
633
  err?: boolean;
@@ -633,6 +648,13 @@ interface RequestRecord {
633
648
  tool: number;
634
649
  total: number;
635
650
  prompt?: number;
651
+ /**
652
+ * Skill-machinery tokens of this request (the `skill` composition
653
+ * category — catalog digests, invocation instructions, `skill`-tool
654
+ * loads). Always written by the current fold; absent on rows folded
655
+ * before the category existed (read as 0).
656
+ */
657
+ skill?: number;
636
658
  /**
637
659
  * Billed cache-read (served) prompt tokens of this request — the
638
660
  * hit-rate numerator against `prompt` (input + cacheRead + cacheWrite).
package/lib/index.js CHANGED
@@ -594,10 +594,10 @@ function toolCallNames(blocks) {
594
594
  * context provenance (client-runtime context-provenance.ts): workspace
595
595
  * instructions name the files they were reconciled from, a plugin source its
596
596
  * plugin id, and any other producer its own durable kind. Returns '' when
597
- * the source carries no readable identity at all.
597
+ * the source is missing or carries no readable identity at all.
598
598
  */
599
599
  function injectionSourceName(source) {
600
- if (source.kind === "agent-instructions" && Array.isArray(source.changes)) {
600
+ if (source?.kind === "agent-instructions" && Array.isArray(source.changes)) {
601
601
  const paths = [];
602
602
  for (const change of source.changes) {
603
603
  const path = change?.path;
@@ -605,8 +605,10 @@ function injectionSourceName(source) {
605
605
  }
606
606
  if (paths.length > 0) return paths.join(", ");
607
607
  }
608
- if (typeof source.plugin === "string" && source.plugin !== "") return source.plugin;
609
- return typeof source.kind === "string" && source.kind !== "" ? source.kind : "";
608
+ const plugin = source?.plugin;
609
+ if (typeof plugin === "string" && plugin !== "") return plugin;
610
+ const kind = source?.kind;
611
+ return typeof kind === "string" && kind !== "" ? kind : "";
610
612
  }
611
613
  function isInjection(source) {
612
614
  return source !== null && source !== void 0 && (typeof source.kind === "string" && source.kind !== "" && source.kind !== "user" || typeof source.form === "string");
@@ -1035,6 +1037,7 @@ function createTimelineState() {
1035
1037
  sums: {
1036
1038
  user: 0,
1037
1039
  inject: 0,
1040
+ skill: 0,
1038
1041
  assistant: 0,
1039
1042
  tool: 0
1040
1043
  },
@@ -1050,6 +1053,8 @@ function createTimelineState() {
1050
1053
  function categoryOf(type, message) {
1051
1054
  if (type === "assistant/message") return "assistant";
1052
1055
  if (type === "tool/result") return "tool";
1056
+ const kind = message?.source?.kind;
1057
+ if (kind === "skill-invocation" || kind === "skill-catalog") return "skill";
1053
1058
  if (isInjection(message?.source)) return "inject";
1054
1059
  return "user";
1055
1060
  }
@@ -1624,7 +1629,10 @@ function applyTimeline(state, event, bounds) {
1624
1629
  rec.name = typeof source.name === "string" ? source.name : "?";
1625
1630
  } else {
1626
1631
  const label = injectionSourceName(source);
1627
- if (label !== "") rec.name = label;
1632
+ if (label !== "") {
1633
+ rec.name = label;
1634
+ node.name = label;
1635
+ }
1628
1636
  if (source.form === "notice" && typeof source.summary === "string" && source.summary !== "") rec.detail = source.summary;
1629
1637
  }
1630
1638
  s.events.push(rec);
@@ -1666,6 +1674,10 @@ function applyTimeline(state, event, bounds) {
1666
1674
  const name = skillNameOf(toolMsg);
1667
1675
  if (name !== "") {
1668
1676
  node.skill = name;
1677
+ if (node.tool === void 0) node.tool = "skill";
1678
+ s.sums.tool -= node.tokens;
1679
+ node.cat = "skill";
1680
+ s.sums.skill += node.tokens;
1669
1681
  s.events.push({
1670
1682
  seq: event.seq,
1671
1683
  time: event.time,
@@ -1683,7 +1695,7 @@ function applyTimeline(state, event, bounds) {
1683
1695
  const usage = data?.usage;
1684
1696
  const s = ensure();
1685
1697
  bumpDetailRev(s);
1686
- const total = s.systemTokens + s.toolsTokens + s.sums.user + s.sums.inject + s.sums.assistant + s.sums.tool;
1698
+ const total = s.systemTokens + s.toolsTokens + s.sums.user + s.sums.inject + s.sums.skill + s.sums.assistant + s.sums.tool;
1687
1699
  const record = {
1688
1700
  time: event.time,
1689
1701
  seq: event.seq,
@@ -1691,6 +1703,7 @@ function applyTimeline(state, event, bounds) {
1691
1703
  tools: s.toolsTokens,
1692
1704
  user: s.sums.user,
1693
1705
  inject: s.sums.inject,
1706
+ skill: s.sums.skill,
1694
1707
  assistant: s.sums.assistant,
1695
1708
  tool: s.sums.tool,
1696
1709
  total
@@ -1787,7 +1800,7 @@ function applyTimeline(state, event, bounds) {
1787
1800
  * issue #29).
1788
1801
  */
1789
1802
  function headFieldsOf(state) {
1790
- const surfaceTotal = state.sums.user + state.sums.inject + state.sums.assistant + state.sums.tool;
1803
+ const surfaceTotal = state.sums.user + state.sums.inject + state.sums.skill + state.sums.assistant + state.sums.tool;
1791
1804
  const result = {
1792
1805
  ok: true,
1793
1806
  ...state.model !== void 0 ? { model: state.model } : {},
@@ -1798,12 +1811,13 @@ function headFieldsOf(state) {
1798
1811
  tools: state.toolsTokens,
1799
1812
  user: state.sums.user,
1800
1813
  inject: state.sums.inject,
1814
+ skill: state.sums.skill,
1801
1815
  assistant: state.sums.assistant,
1802
1816
  tool: state.sums.tool,
1803
1817
  total: surfaceTotal + state.systemTokens + state.toolsTokens
1804
1818
  },
1805
1819
  images: state.surface.reduce((n, node) => n + (node.imgs ?? 0), 0),
1806
- toolCalls: state.surface.reduce((n, node) => node.cat === "tool" ? n + 1 : n, 0),
1820
+ toolCalls: state.surface.reduce((n, node) => node.cat === "tool" || node.cat === "skill" && node.tool !== void 0 ? n + 1 : n, 0),
1807
1821
  humanInputs: state.humanInputs ?? 0,
1808
1822
  requests: [],
1809
1823
  events: [],
@@ -1857,12 +1871,12 @@ function detailCollectionsOf(state, bounds) {
1857
1871
  const overflowCount = Math.max(0, state.surface.length - bounds.maxNodes);
1858
1872
  const overflow = state.surface.slice(0, overflowCount);
1859
1873
  const tail = state.surface.slice(overflowCount);
1860
- const pinned = overflow.filter((n) => n.cat === "inject");
1874
+ const pinned = overflow.filter((n) => n.cat === "inject" || n.cat === "skill");
1861
1875
  result.nodes = pinned.length > 0 ? [...pinned, ...tail] : tail;
1862
1876
  result.droppedNodes = overflowCount - pinned.length;
1863
1877
  if (result.droppedNodes > 0) {
1864
1878
  let floor = 0;
1865
- for (const n of overflow) if (n.cat !== "inject") floor = Math.max(floor, n.seq);
1879
+ for (const n of overflow) if (n.cat !== "inject" && n.cat !== "skill") floor = Math.max(floor, n.seq);
1866
1880
  result.surfaceFloor = floor;
1867
1881
  }
1868
1882
  if (state.archiveFloor !== void 0) result.archiveFloor = state.archiveFloor;
@@ -2259,6 +2273,7 @@ const surfaceNodeSchema = z.object({
2259
2273
  cat: z.enum([
2260
2274
  "user",
2261
2275
  "inject",
2276
+ "skill",
2262
2277
  "assistant",
2263
2278
  "tool"
2264
2279
  ]),
@@ -2266,6 +2281,7 @@ const surfaceNodeSchema = z.object({
2266
2281
  imgs: z.number().int().nonnegative().optional(),
2267
2282
  gone: z.number().int().nonnegative().optional(),
2268
2283
  form: z.string().optional(),
2284
+ name: z.string().optional(),
2269
2285
  text: z.string().optional(),
2270
2286
  tool: z.string().optional(),
2271
2287
  err: z.boolean().optional(),
@@ -2291,6 +2307,11 @@ const requestRecordSchema = z.object({
2291
2307
  tool: z.number().int().nonnegative(),
2292
2308
  total: z.number().int().nonnegative(),
2293
2309
  prompt: z.number().int().nonnegative().optional(),
2310
+ /**
2311
+ * Skill-machinery tokens (issue #66). The fold writes it on every record;
2312
+ * optional so rows folded before the category existed still parse.
2313
+ */
2314
+ skill: z.number().int().nonnegative().optional(),
2294
2315
  cacheRead: z.number().int().nonnegative().optional(),
2295
2316
  output: z.number().int().nonnegative().optional(),
2296
2317
  stepCount: z.number().int().positive().optional()
@@ -2351,6 +2372,7 @@ const currentSchema = z.object({
2351
2372
  tools: z.number().int().nonnegative(),
2352
2373
  user: z.number().int().nonnegative(),
2353
2374
  inject: z.number().int().nonnegative(),
2375
+ skill: z.number().int().nonnegative(),
2354
2376
  assistant: z.number().int().nonnegative(),
2355
2377
  tool: z.number().int().nonnegative(),
2356
2378
  total: z.number().int().nonnegative()
@@ -2447,6 +2469,7 @@ const timelineStateSchema = z.object({
2447
2469
  sums: z.object({
2448
2470
  user: z.number().int().nonnegative(),
2449
2471
  inject: z.number().int().nonnegative(),
2472
+ skill: z.number().int().nonnegative(),
2450
2473
  assistant: z.number().int().nonnegative(),
2451
2474
  tool: z.number().int().nonnegative()
2452
2475
  }).strict(),
@@ -2539,7 +2562,7 @@ function createContextTimelineDefinition(config, slim) {
2539
2562
  },
2540
2563
  init: () => createTimelineState(),
2541
2564
  apply: (state, event) => applyTimeline(state, event, bounds),
2542
- stateVersion: 18
2565
+ stateVersion: 19
2543
2566
  };
2544
2567
  }
2545
2568
  //#endregion
@@ -2611,6 +2634,7 @@ function createFallbackTimelineDefinition(current) {
2611
2634
  tools: 0,
2612
2635
  user: 0,
2613
2636
  inject: 0,
2637
+ skill: 0,
2614
2638
  assistant: 0,
2615
2639
  tool: 0,
2616
2640
  total: 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.51.1",
3
+ "version": "0.52.1",
4
4
  "description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {
@@ -45,7 +45,7 @@
45
45
  "coverage": "vitest run --coverage",
46
46
  "release": "bash scripts/publish.sh",
47
47
  "social-preview": "node docs/social-preview/generate.ts",
48
- "prepare": "husky"
48
+ "prepare": "husky && tsdown"
49
49
  },
50
50
  "dsh": {
51
51
  "bundle": {