dsh-context 0.36.0 → 0.38.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 +5 -1
- package/lib/client.js +718 -83
- package/lib/index.d.ts +149 -317
- package/lib/index.js +125 -10
- package/package.json +9 -10
package/lib/index.js
CHANGED
|
@@ -786,10 +786,18 @@ function applySurface(st, ev, type, data, message) {
|
|
|
786
786
|
}
|
|
787
787
|
} else if (type === "tool/result") {
|
|
788
788
|
const srcId = source?.callId;
|
|
789
|
-
const srcName = typeof srcId === "string" ? st.callNames[srcId] : void 0;
|
|
790
789
|
const blockId = (message?.content?.[0])?.toolCallId;
|
|
791
|
-
|
|
792
|
-
|
|
790
|
+
const srcEntry = typeof srcId === "string" ? st.callNames[srcId] : void 0;
|
|
791
|
+
const blockEntry = srcEntry === void 0 && typeof blockId === "string" ? st.callNames[blockId] : void 0;
|
|
792
|
+
const toolEntry = srcEntry ?? blockEntry;
|
|
793
|
+
if (toolEntry !== void 0) {
|
|
794
|
+
node.tool = toolEntry.name;
|
|
795
|
+
const timing = ensureTiming(st);
|
|
796
|
+
const dur = durOf(toolEntry.start, ev.time);
|
|
797
|
+
timing.toolsMs += dur;
|
|
798
|
+
timing.toolCalls += 1;
|
|
799
|
+
bumpToolTotals(timing, toolEntry.name, dur);
|
|
800
|
+
}
|
|
793
801
|
if (typeof srcId === "string" || typeof blockId === "string") {
|
|
794
802
|
const kept = {};
|
|
795
803
|
for (const k in st.callNames) if (k !== srcId && k !== blockId) kept[k] = st.callNames[k];
|
|
@@ -922,6 +930,60 @@ function accumulateCost(st, time, usage) {
|
|
|
922
930
|
* `bounds` come from the plugin config (config.ts) — retention only, they
|
|
923
931
|
* never change the state shape.
|
|
924
932
|
*/
|
|
933
|
+
/** The timing card's per-tool ranking cap: the busiest 16 names are kept. */
|
|
934
|
+
const TOOL_TIMING_CAP = 16;
|
|
935
|
+
/** Non-negative, NaN-proof duration between two instants (hostile times degrade to 0). */
|
|
936
|
+
function durOf(from, to) {
|
|
937
|
+
if (!Number.isFinite(from) || !Number.isFinite(to)) return 0;
|
|
938
|
+
return Math.max(0, to - from);
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* The fold's private timing accumulator: created on first use, and CLONED on
|
|
942
|
+
* every later ensure() (see `applyTimeline`) — the object left in the
|
|
943
|
+
* persisted previous state is never written into in place.
|
|
944
|
+
*/
|
|
945
|
+
function ensureTiming(st) {
|
|
946
|
+
if (st.timing === void 0) st.timing = {
|
|
947
|
+
wallMs: 0,
|
|
948
|
+
lmMs: 0,
|
|
949
|
+
calls: 0,
|
|
950
|
+
toolsMs: 0,
|
|
951
|
+
toolCalls: 0,
|
|
952
|
+
tools: {}
|
|
953
|
+
};
|
|
954
|
+
return st.timing;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Tally one completed tool call into the per-name ranking, bounded to
|
|
958
|
+
* TOOL_TIMING_CAP names: repeated names update in place, a new name beyond
|
|
959
|
+
* the cap evicts the smallest tally first (the ranking's tail), so state
|
|
960
|
+
* stays bounded even over a hostile log of unique names.
|
|
961
|
+
*/
|
|
962
|
+
function bumpToolTotals(timing, name, ms) {
|
|
963
|
+
if (!Object.hasOwn(timing.tools, name)) {
|
|
964
|
+
if (Object.keys(timing.tools).length >= TOOL_TIMING_CAP) {
|
|
965
|
+
let minKey = "";
|
|
966
|
+
let minMs = Infinity;
|
|
967
|
+
for (const k in timing.tools) if (timing.tools[k].ms < minMs) {
|
|
968
|
+
minMs = timing.tools[k].ms;
|
|
969
|
+
minKey = k;
|
|
970
|
+
}
|
|
971
|
+
const kept = {};
|
|
972
|
+
for (const k in timing.tools) if (k !== minKey) kept[k] = timing.tools[k];
|
|
973
|
+
timing.tools = kept;
|
|
974
|
+
}
|
|
975
|
+
timing.tools[name] = {
|
|
976
|
+
calls: 1,
|
|
977
|
+
ms
|
|
978
|
+
};
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
const cur = timing.tools[name];
|
|
982
|
+
timing.tools[name] = {
|
|
983
|
+
calls: cur.calls + 1,
|
|
984
|
+
ms: cur.ms + ms
|
|
985
|
+
};
|
|
986
|
+
}
|
|
925
987
|
function applyTimeline(state, event, bounds) {
|
|
926
988
|
let st;
|
|
927
989
|
const ensure = () => st ??= {
|
|
@@ -931,7 +993,11 @@ function applyTimeline(state, event, bounds) {
|
|
|
931
993
|
requests: [...state.requests],
|
|
932
994
|
events: [...state.events],
|
|
933
995
|
archived: [...state.archived],
|
|
934
|
-
callNames: { ...state.callNames }
|
|
996
|
+
callNames: { ...state.callNames },
|
|
997
|
+
...state.timing !== void 0 ? { timing: {
|
|
998
|
+
...state.timing,
|
|
999
|
+
tools: { ...state.timing.tools }
|
|
1000
|
+
} } : {}
|
|
935
1001
|
};
|
|
936
1002
|
const data = event.data;
|
|
937
1003
|
try {
|
|
@@ -964,9 +1030,25 @@ function applyTimeline(state, event, bounds) {
|
|
|
964
1030
|
case "tool/call":
|
|
965
1031
|
if (data && typeof data.callId === "string" && typeof data.name === "string") {
|
|
966
1032
|
const s = ensure();
|
|
967
|
-
s.callNames[data.callId] =
|
|
1033
|
+
s.callNames[data.callId] = {
|
|
1034
|
+
name: data.name,
|
|
1035
|
+
start: event.time
|
|
1036
|
+
};
|
|
968
1037
|
}
|
|
969
1038
|
break;
|
|
1039
|
+
case "step/start": {
|
|
1040
|
+
const s = ensure();
|
|
1041
|
+
s.stepStart = { time: event.time };
|
|
1042
|
+
break;
|
|
1043
|
+
}
|
|
1044
|
+
case "step/end": {
|
|
1045
|
+
const start = state.stepStart;
|
|
1046
|
+
if (start === void 0) return state;
|
|
1047
|
+
const s = ensure();
|
|
1048
|
+
ensureTiming(s).wallMs += durOf(start.time, event.time);
|
|
1049
|
+
delete s.stepStart;
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
970
1052
|
case "user/message": {
|
|
971
1053
|
const msg = deriveEventMessage(event);
|
|
972
1054
|
const s = ensure();
|
|
@@ -1037,6 +1119,10 @@ function applyTimeline(state, event, bounds) {
|
|
|
1037
1119
|
accumulateCost(s, event.time, usage);
|
|
1038
1120
|
}
|
|
1039
1121
|
s.requests.push(record);
|
|
1122
|
+
const timing = ensureTiming(s);
|
|
1123
|
+
timing.calls += 1;
|
|
1124
|
+
const stepStart = state.stepStart;
|
|
1125
|
+
if (stepStart !== void 0) timing.lmMs += durOf(stepStart.time, event.time);
|
|
1040
1126
|
const asstMsg = deriveEventMessage(event);
|
|
1041
1127
|
applySurface(s, event, event.type, data, asstMsg);
|
|
1042
1128
|
break;
|
|
@@ -1085,9 +1171,9 @@ function buildTimelineView(state, bounds) {
|
|
|
1085
1171
|
const surfaceTotal = state.sums.user + state.sums.inject + state.sums.assistant + state.sums.tool;
|
|
1086
1172
|
const result = {
|
|
1087
1173
|
ok: true,
|
|
1088
|
-
model: state.model,
|
|
1089
|
-
provider: state.provider,
|
|
1090
|
-
contextWindow: state.contextWindow,
|
|
1174
|
+
...state.model !== void 0 ? { model: state.model } : {},
|
|
1175
|
+
...state.provider !== void 0 ? { provider: state.provider } : {},
|
|
1176
|
+
...state.contextWindow !== void 0 ? { contextWindow: state.contextWindow } : {},
|
|
1091
1177
|
current: {
|
|
1092
1178
|
system: state.systemTokens,
|
|
1093
1179
|
tools: state.toolsTokens,
|
|
@@ -1120,6 +1206,14 @@ function buildTimelineView(state, bounds) {
|
|
|
1120
1206
|
if (pro !== void 0) cost.pro = pro;
|
|
1121
1207
|
result.cost = cost;
|
|
1122
1208
|
}
|
|
1209
|
+
if (state.timing !== void 0) {
|
|
1210
|
+
const tools = {};
|
|
1211
|
+
for (const k in state.timing.tools) tools[k] = { ...state.timing.tools[k] };
|
|
1212
|
+
result.timing = {
|
|
1213
|
+
...state.timing,
|
|
1214
|
+
tools
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1123
1217
|
const overflowCount = Math.max(0, state.surface.length - bounds.maxNodes);
|
|
1124
1218
|
const overflow = state.surface.slice(0, overflowCount);
|
|
1125
1219
|
const tail = state.surface.slice(overflowCount);
|
|
@@ -1249,6 +1343,18 @@ const costFamilySchema = z.object({
|
|
|
1249
1343
|
peak: costBucketsSchema.optional(),
|
|
1250
1344
|
off: costBucketsSchema.optional()
|
|
1251
1345
|
}).strict();
|
|
1346
|
+
const toolTimingSchema = z.object({
|
|
1347
|
+
calls: z.number().int().nonnegative(),
|
|
1348
|
+
ms: z.number().nonnegative()
|
|
1349
|
+
}).strict();
|
|
1350
|
+
const timingTotalsSchema = z.object({
|
|
1351
|
+
wallMs: z.number().nonnegative(),
|
|
1352
|
+
lmMs: z.number().nonnegative(),
|
|
1353
|
+
calls: z.number().int().nonnegative(),
|
|
1354
|
+
toolsMs: z.number().nonnegative(),
|
|
1355
|
+
toolCalls: z.number().int().nonnegative(),
|
|
1356
|
+
tools: z.record(z.string(), toolTimingSchema)
|
|
1357
|
+
}).strict();
|
|
1252
1358
|
const contextTimelineSchema = z.object({
|
|
1253
1359
|
ok: z.literal(true),
|
|
1254
1360
|
model: z.string().optional(),
|
|
@@ -1263,6 +1369,7 @@ const contextTimelineSchema = z.object({
|
|
|
1263
1369
|
flash: costFamilySchema.optional(),
|
|
1264
1370
|
pro: costFamilySchema.optional()
|
|
1265
1371
|
}).strict().optional(),
|
|
1372
|
+
timing: timingTotalsSchema.optional(),
|
|
1266
1373
|
nodes: z.array(surfaceNodeSchema),
|
|
1267
1374
|
droppedNodes: z.number().int().nonnegative(),
|
|
1268
1375
|
archive: z.array(surfaceNodeSchema),
|
|
@@ -1297,7 +1404,12 @@ const timelineStateSchema = z.object({
|
|
|
1297
1404
|
pro: costFamilySchema.optional()
|
|
1298
1405
|
}).strict().optional(),
|
|
1299
1406
|
archiveFloor: z.number().optional(),
|
|
1300
|
-
|
|
1407
|
+
timing: timingTotalsSchema.optional(),
|
|
1408
|
+
stepStart: z.object({ time: z.number() }).strict().optional(),
|
|
1409
|
+
callNames: z.record(z.string(), z.object({
|
|
1410
|
+
name: z.string(),
|
|
1411
|
+
start: z.number()
|
|
1412
|
+
}).strict()),
|
|
1301
1413
|
pendingShadowedSeqs: z.array(z.number()).optional(),
|
|
1302
1414
|
pendingShadowEventSeq: z.number().optional()
|
|
1303
1415
|
});
|
|
@@ -1314,6 +1426,9 @@ const timelineStateSchema = z.object({
|
|
|
1314
1426
|
* The definition carries BOTH session-projection contracts (see compat.ts):
|
|
1315
1427
|
* `schema`/`view` for dsh <= 0.1.0-rc.8, `stateSchema`/`wire` for
|
|
1316
1428
|
* dsh >= 0.1.1-rc.1 — each registry reads its own fields off the same unit.
|
|
1429
|
+
* (The return type is the mirrored dual contract, not the installed dts
|
|
1430
|
+
* `ProjectionDefinition`: the 0.1.1+ registry's wired-register overload
|
|
1431
|
+
* demands `wire` PRESENT, which the dts's optional `wire?` fails.)
|
|
1317
1432
|
* Without the `wire` block the 0.1.1-rc.1+ registry treats the unit as
|
|
1318
1433
|
* host-only and never delivers `contextTimeline` to the browser (the Context
|
|
1319
1434
|
* tab would stay on its loading screen forever).
|
|
@@ -1332,7 +1447,7 @@ function createContextTimelineDefinition(config) {
|
|
|
1332
1447
|
},
|
|
1333
1448
|
init: () => createTimelineState(),
|
|
1334
1449
|
apply: (state, event) => applyTimeline(state, event, bounds),
|
|
1335
|
-
stateVersion:
|
|
1450
|
+
stateVersion: 11
|
|
1336
1451
|
};
|
|
1337
1452
|
}
|
|
1338
1453
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.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": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"build": "tsdown",
|
|
35
35
|
"lint": "oxlint",
|
|
36
36
|
"lint:fix": "oxlint --fix",
|
|
37
|
-
"watch": "
|
|
37
|
+
"watch": "bash scripts/watch.sh",
|
|
38
38
|
"register": "bash scripts/register.sh",
|
|
39
39
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.tests.json",
|
|
40
40
|
"test": "pnpm run typecheck && vitest run --coverage",
|
|
@@ -52,7 +52,6 @@
|
|
|
52
52
|
"inject": [
|
|
53
53
|
"@deepseek-ai/dsh-client-connection",
|
|
54
54
|
"@deepseek-ai/dsh-client-locale",
|
|
55
|
-
"@deepseek-ai/dsh-client-runtime",
|
|
56
55
|
"@deepseek-ai/dsh-client-ui-conversation",
|
|
57
56
|
"@deepseek-ai/dsh-client-ui-settings"
|
|
58
57
|
],
|
|
@@ -71,9 +70,9 @@
|
|
|
71
70
|
"license": "Apache-2.0",
|
|
72
71
|
"peerDependencies": {
|
|
73
72
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
74
|
-
"@deepseek-ai/dsh-client-ui-primitives": "
|
|
75
|
-
"@deepseek-ai/dsh-session": "
|
|
76
|
-
"@deepseek-ai/dsh-settings": "
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-primitives": ">=0.1.0-rc.7",
|
|
74
|
+
"@deepseek-ai/dsh-session": ">=0.1.0-rc.7",
|
|
75
|
+
"@deepseek-ai/dsh-settings": ">=0.1.0-rc.7",
|
|
77
76
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
78
77
|
"react": "^18.3.1",
|
|
79
78
|
"zod": "^4.4.3"
|
|
@@ -88,10 +87,10 @@
|
|
|
88
87
|
},
|
|
89
88
|
"devDependencies": {
|
|
90
89
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
91
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.
|
|
92
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
93
|
-
"@deepseek-ai/dsh-session-projection": "^0.1.
|
|
94
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.1-rc.2",
|
|
91
|
+
"@deepseek-ai/dsh-session": "^0.1.1-rc.2",
|
|
92
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.1-rc.2",
|
|
93
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
95
94
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
96
95
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
97
96
|
"@types/node": "^26.2.0",
|