dsh-context 0.34.0 → 0.35.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/README.md +3 -1
- package/lib/client.js +926 -68
- package/lib/index.js +171 -143
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -190,27 +190,37 @@ const ROLE_OVERHEAD = 4;
|
|
|
190
190
|
function estimateToolsTotal(tools) {
|
|
191
191
|
return tools.length > 0 ? Math.ceil(JSON.stringify(tools).length / CHARS_PER_TOKEN) + BLOCK_OVERHEAD : 0;
|
|
192
192
|
}
|
|
193
|
+
/** The `ContentBlock` walkers take `unknown`: block arrays ride the untrusted
|
|
194
|
+
* log, so their element shapes (null and primitives included) are re-proved
|
|
195
|
+
* here, not trusted from the declared message types. */
|
|
193
196
|
function estimateBlocks(blocks) {
|
|
194
197
|
let tokens = 0;
|
|
195
198
|
if (!Array.isArray(blocks)) return 0;
|
|
196
|
-
for (const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
199
|
+
for (const item of blocks) {
|
|
200
|
+
if (item === null || typeof item !== "object") {
|
|
201
|
+
tokens += BLOCK_OVERHEAD;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const block = item;
|
|
205
|
+
switch (block.type) {
|
|
206
|
+
case "text":
|
|
207
|
+
case "reasoning":
|
|
208
|
+
tokens += Math.ceil((block.text || "").length / CHARS_PER_TOKEN) + BLOCK_OVERHEAD;
|
|
209
|
+
break;
|
|
210
|
+
case "tool-call":
|
|
211
|
+
tokens += Math.ceil((block.name || "").length / CHARS_PER_TOKEN) + Math.ceil((block.arguments || "").length / CHARS_PER_TOKEN) + BLOCK_OVERHEAD;
|
|
212
|
+
break;
|
|
213
|
+
case "tool-result":
|
|
214
|
+
tokens += estimateBlocks(block.content) + BLOCK_OVERHEAD;
|
|
215
|
+
break;
|
|
216
|
+
case "image": {
|
|
217
|
+
const ref = block.attachment;
|
|
218
|
+
const priced = ref !== null && typeof ref === "object" && typeof ref.width === "number" && typeof ref.height === "number" ? estimateImageTokens(ref.width, ref.height) : null;
|
|
219
|
+
tokens += (priced ?? Math.ceil(JSON.stringify(block).length / CHARS_PER_TOKEN)) + BLOCK_OVERHEAD;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
default: tokens += BLOCK_OVERHEAD + Math.ceil(JSON.stringify(block).length / CHARS_PER_TOKEN);
|
|
212
223
|
}
|
|
213
|
-
default: tokens += BLOCK_OVERHEAD + Math.ceil(JSON.stringify(block).length / CHARS_PER_TOKEN);
|
|
214
224
|
}
|
|
215
225
|
return tokens;
|
|
216
226
|
}
|
|
@@ -238,19 +248,31 @@ function estimateToolSchema(tool) {
|
|
|
238
248
|
function imageCountOf(blocks) {
|
|
239
249
|
let count = 0;
|
|
240
250
|
if (!Array.isArray(blocks)) return 0;
|
|
241
|
-
for (const
|
|
242
|
-
|
|
251
|
+
for (const item of blocks) {
|
|
252
|
+
if (item === null || typeof item !== "object") continue;
|
|
253
|
+
const block = item;
|
|
254
|
+
if (block.type === "image") count++;
|
|
255
|
+
else if (Array.isArray(block.content)) count += imageCountOf(block.content);
|
|
256
|
+
}
|
|
243
257
|
return count;
|
|
244
258
|
}
|
|
245
259
|
function firstText(blocks) {
|
|
246
260
|
if (!Array.isArray(blocks)) return "";
|
|
247
|
-
for (const
|
|
261
|
+
for (const item of blocks) {
|
|
262
|
+
if (item === null || typeof item !== "object") continue;
|
|
263
|
+
const b = item;
|
|
264
|
+
if (b.type === "text" && typeof b.text === "string" && b.text.trim() !== "") return b.text.replace(/\s+/g, " ").trim().slice(0, 80);
|
|
265
|
+
}
|
|
248
266
|
return "";
|
|
249
267
|
}
|
|
250
268
|
function toolCallNames(blocks) {
|
|
251
269
|
const names = [];
|
|
252
270
|
if (!Array.isArray(blocks)) return names;
|
|
253
|
-
for (const
|
|
271
|
+
for (const item of blocks) {
|
|
272
|
+
if (item === null || typeof item !== "object") continue;
|
|
273
|
+
const b = item;
|
|
274
|
+
if (b.type === "tool-call" && typeof b.name === "string") names.push(b.name);
|
|
275
|
+
}
|
|
254
276
|
return names;
|
|
255
277
|
}
|
|
256
278
|
/**
|
|
@@ -323,7 +345,7 @@ function recordOf(event) {
|
|
|
323
345
|
seq: event.seq,
|
|
324
346
|
time: event.time,
|
|
325
347
|
tools: tools.map((t) => {
|
|
326
|
-
const tool = t;
|
|
348
|
+
const tool = t !== null && typeof t === "object" ? t : {};
|
|
327
349
|
const entry = {
|
|
328
350
|
name: typeof tool.name === "string" ? tool.name : "?",
|
|
329
351
|
tokens: estimateToolSchema(t),
|
|
@@ -471,7 +493,9 @@ function archiveRemoved(st, removed, goneSeq) {
|
|
|
471
493
|
*/
|
|
472
494
|
function nestedText(blocks) {
|
|
473
495
|
if (!Array.isArray(blocks)) return "";
|
|
474
|
-
for (const
|
|
496
|
+
for (const item of blocks) {
|
|
497
|
+
if (item === null || typeof item !== "object") continue;
|
|
498
|
+
const block = item;
|
|
475
499
|
if (block.type === "text" && typeof block.text === "string" && block.text !== "") return block.text;
|
|
476
500
|
if (block.content !== void 0) {
|
|
477
501
|
const nested = nestedText(block.content);
|
|
@@ -513,8 +537,8 @@ function applySurface(st, ev, type, data, message) {
|
|
|
513
537
|
const srcId = source?.callId;
|
|
514
538
|
const srcName = typeof srcId === "string" ? st.callNames[srcId] : void 0;
|
|
515
539
|
const blockId = (message?.content?.[0])?.toolCallId;
|
|
516
|
-
if (srcName) node.tool = srcName;
|
|
517
|
-
else if (typeof blockId === "string") node.tool = st.callNames[blockId];
|
|
540
|
+
if (srcName !== void 0) node.tool = srcName;
|
|
541
|
+
else if (typeof blockId === "string" && Object.hasOwn(st.callNames, blockId)) node.tool = st.callNames[blockId];
|
|
518
542
|
if (typeof srcId === "string" || typeof blockId === "string") {
|
|
519
543
|
const kept = {};
|
|
520
544
|
for (const k in st.callNames) if (k !== srcId && k !== blockId) kept[k] = st.callNames[k];
|
|
@@ -659,137 +683,141 @@ function applyTimeline(state, event, bounds) {
|
|
|
659
683
|
callNames: { ...state.callNames }
|
|
660
684
|
};
|
|
661
685
|
const data = event.data;
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
s.toolsTokens = estimateToolsTotal(tools);
|
|
668
|
-
s.systemTokens = estimateSystem(header.system);
|
|
669
|
-
if (header.config && typeof header.config.model === "string") s.model = header.config.model;
|
|
670
|
-
if (header.config && typeof header.config.provider === "string") s.provider = header.config.provider;
|
|
671
|
-
if ((data?.reason === "change" || data?.reason === "resume") && s.model && s.lastModel && s.model !== s.lastModel) s.events.push({
|
|
672
|
-
seq: event.seq,
|
|
673
|
-
time: event.time,
|
|
674
|
-
kind: "model",
|
|
675
|
-
from: s.lastModel,
|
|
676
|
-
to: s.model
|
|
677
|
-
});
|
|
678
|
-
if (s.model) s.lastModel = s.model;
|
|
679
|
-
break;
|
|
680
|
-
}
|
|
681
|
-
case "request/context": {
|
|
682
|
-
const s = ensure();
|
|
683
|
-
if (data && typeof data.contextWindow === "number") s.contextWindow = data.contextWindow;
|
|
684
|
-
if (data && typeof data.model === "string") s.model = data.model;
|
|
685
|
-
if (data && typeof data.provider === "string") s.provider = data.provider;
|
|
686
|
-
break;
|
|
687
|
-
}
|
|
688
|
-
case "tool/call":
|
|
689
|
-
if (data && typeof data.callId === "string" && typeof data.name === "string") {
|
|
686
|
+
try {
|
|
687
|
+
switch (event.type) {
|
|
688
|
+
case "request/header": {
|
|
689
|
+
const header = data?.header ?? {};
|
|
690
|
+
const tools = Array.isArray(header.tools) ? header.tools : [];
|
|
690
691
|
const s = ensure();
|
|
691
|
-
s.
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
const s = ensure();
|
|
697
|
-
const node = applySurface(s, event, event.type, data, msg);
|
|
698
|
-
const source = msg?.source;
|
|
699
|
-
if (isInjection(source)) {
|
|
700
|
-
const rec = {
|
|
692
|
+
s.toolsTokens = estimateToolsTotal(tools);
|
|
693
|
+
s.systemTokens = estimateSystem(header.system);
|
|
694
|
+
if (header.config && typeof header.config.model === "string") s.model = header.config.model;
|
|
695
|
+
if (header.config && typeof header.config.provider === "string") s.provider = header.config.provider;
|
|
696
|
+
if ((data?.reason === "change" || data?.reason === "resume") && s.model && s.lastModel && s.model !== s.lastModel) s.events.push({
|
|
701
697
|
seq: event.seq,
|
|
702
698
|
time: event.time,
|
|
703
|
-
kind: "
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
};
|
|
707
|
-
if (
|
|
708
|
-
|
|
709
|
-
rec.name = typeof source.name === "string" ? source.name : "?";
|
|
710
|
-
} else {
|
|
711
|
-
const label = injectionSourceName(source);
|
|
712
|
-
if (label !== "") rec.name = label;
|
|
713
|
-
if (source.form === "notice" && typeof source.summary === "string" && source.summary !== "") rec.detail = source.summary;
|
|
714
|
-
}
|
|
715
|
-
s.events.push(rec);
|
|
699
|
+
kind: "model",
|
|
700
|
+
from: s.lastModel,
|
|
701
|
+
to: s.model
|
|
702
|
+
});
|
|
703
|
+
if (s.model) s.lastModel = s.model;
|
|
704
|
+
break;
|
|
716
705
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
if (name
|
|
726
|
-
|
|
727
|
-
s.
|
|
706
|
+
case "request/context": {
|
|
707
|
+
const s = ensure();
|
|
708
|
+
if (data && typeof data.contextWindow === "number") s.contextWindow = data.contextWindow;
|
|
709
|
+
if (data && typeof data.model === "string") s.model = data.model;
|
|
710
|
+
if (data && typeof data.provider === "string") s.provider = data.provider;
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
case "tool/call":
|
|
714
|
+
if (data && typeof data.callId === "string" && typeof data.name === "string") {
|
|
715
|
+
const s = ensure();
|
|
716
|
+
s.callNames[data.callId] = data.name;
|
|
717
|
+
}
|
|
718
|
+
break;
|
|
719
|
+
case "user/message": {
|
|
720
|
+
const msg = deriveEventMessage(event);
|
|
721
|
+
const s = ensure();
|
|
722
|
+
const node = applySurface(s, event, event.type, data, msg);
|
|
723
|
+
const source = msg?.source;
|
|
724
|
+
if (isInjection(source)) {
|
|
725
|
+
const rec = {
|
|
728
726
|
seq: event.seq,
|
|
729
727
|
time: event.time,
|
|
730
728
|
kind: "inject",
|
|
731
|
-
form: "
|
|
732
|
-
sub: "skill",
|
|
733
|
-
name,
|
|
729
|
+
form: source.form || "context",
|
|
734
730
|
tokens: node.tokens
|
|
735
|
-
}
|
|
731
|
+
};
|
|
732
|
+
if (source.kind === "skill-invocation") {
|
|
733
|
+
rec.sub = "skill";
|
|
734
|
+
rec.name = typeof source.name === "string" ? source.name : "?";
|
|
735
|
+
} else {
|
|
736
|
+
const label = injectionSourceName(source);
|
|
737
|
+
if (label !== "") rec.name = label;
|
|
738
|
+
if (source.form === "notice" && typeof source.summary === "string" && source.summary !== "") rec.detail = source.summary;
|
|
739
|
+
}
|
|
740
|
+
s.events.push(rec);
|
|
736
741
|
}
|
|
742
|
+
break;
|
|
737
743
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
record.prompt = usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
|
|
759
|
-
if (typeof usage.cacheReadTokens === "number") record.cacheRead = usage.cacheReadTokens;
|
|
760
|
-
if (typeof usage.outputTokens === "number") record.output = usage.outputTokens;
|
|
761
|
-
accumulateCost(s, event.time, usage);
|
|
744
|
+
case "tool/result": {
|
|
745
|
+
const toolMsg = deriveEventMessage(event);
|
|
746
|
+
const s = ensure();
|
|
747
|
+
const node = applySurface(s, event, event.type, data, toolMsg);
|
|
748
|
+
if (node.tool === "skill" || node.tool === void 0) {
|
|
749
|
+
const name = skillNameOf(toolMsg);
|
|
750
|
+
if (name !== "") {
|
|
751
|
+
node.skill = name;
|
|
752
|
+
s.events.push({
|
|
753
|
+
seq: event.seq,
|
|
754
|
+
time: event.time,
|
|
755
|
+
kind: "inject",
|
|
756
|
+
form: "instructions",
|
|
757
|
+
sub: "skill",
|
|
758
|
+
name,
|
|
759
|
+
tokens: node.tokens
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
break;
|
|
762
764
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
765
|
+
case "assistant/message": {
|
|
766
|
+
const usage = data?.usage;
|
|
767
|
+
const s = ensure();
|
|
768
|
+
const total = s.systemTokens + s.toolsTokens + s.sums.user + s.sums.inject + s.sums.assistant + s.sums.tool;
|
|
769
|
+
const record = {
|
|
770
|
+
time: event.time,
|
|
771
|
+
seq: event.seq,
|
|
772
|
+
system: s.systemTokens,
|
|
773
|
+
tools: s.toolsTokens,
|
|
774
|
+
user: s.sums.user,
|
|
775
|
+
inject: s.sums.inject,
|
|
776
|
+
assistant: s.sums.assistant,
|
|
777
|
+
tool: s.sums.tool,
|
|
778
|
+
total
|
|
779
|
+
};
|
|
780
|
+
if (data && typeof data.turn === "number") record.turn = data.turn;
|
|
781
|
+
if (data && typeof data.step === "number") record.step = data.step;
|
|
782
|
+
if (usage && typeof usage.inputTokens === "number") {
|
|
783
|
+
record.prompt = usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
|
|
784
|
+
if (typeof usage.cacheReadTokens === "number") record.cacheRead = usage.cacheReadTokens;
|
|
785
|
+
if (typeof usage.outputTokens === "number") record.output = usage.outputTokens;
|
|
786
|
+
accumulateCost(s, event.time, usage);
|
|
787
|
+
}
|
|
788
|
+
s.requests.push(record);
|
|
789
|
+
const asstMsg = deriveEventMessage(event);
|
|
790
|
+
applySurface(s, event, event.type, data, asstMsg);
|
|
791
|
+
break;
|
|
782
792
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
793
|
+
case "plan/mode":
|
|
794
|
+
if (data && typeof data.active === "boolean") ensure().events.push({
|
|
795
|
+
seq: event.seq,
|
|
796
|
+
time: event.time,
|
|
797
|
+
kind: "mode",
|
|
798
|
+
name: data.active ? "plan.on" : "plan.off"
|
|
799
|
+
});
|
|
800
|
+
break;
|
|
801
|
+
case "compaction/summary":
|
|
802
|
+
case "compaction/prune": {
|
|
803
|
+
const s = ensure();
|
|
804
|
+
if (data && Array.isArray(data.shadowedSeqs)) {
|
|
805
|
+
s.pendingShadowedSeqs = data.shadowedSeqs.filter((x) => typeof x === "number");
|
|
806
|
+
s.pendingShadowEventSeq = event.seq;
|
|
807
|
+
}
|
|
808
|
+
s.events.push({
|
|
809
|
+
seq: event.seq,
|
|
810
|
+
time: event.time,
|
|
811
|
+
kind: event.type === "compaction/summary" ? "compaction" : "prune",
|
|
812
|
+
tokens: data && typeof data.shadowedTokenCount === "number" ? data.shadowedTokenCount : 0,
|
|
813
|
+
...event.type === "compaction/summary" && data && Array.isArray(data.shadowedSeqs) ? { count: data.shadowedSeqs.length } : {}
|
|
814
|
+
});
|
|
815
|
+
break;
|
|
816
|
+
}
|
|
817
|
+
default: return state;
|
|
791
818
|
}
|
|
792
|
-
|
|
819
|
+
} catch {
|
|
820
|
+
st = void 0;
|
|
793
821
|
}
|
|
794
822
|
if (st !== void 0) {
|
|
795
823
|
trimState(st, bounds);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
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": {
|