dsh-context 0.42.0 → 0.44.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/client.js +1047 -565
- package/lib/index.d.ts +171 -24
- package/lib/index.js +830 -251
- package/package.json +6 -4
package/lib/client.js
CHANGED
|
@@ -20,7 +20,6 @@ window.__ModuleLoader__.load({
|
|
|
20
20
|
"overview.ofUsed": "占已用上下文",
|
|
21
21
|
"overview.compactReserve": "自动压缩预留:占用达 {pct}% 窗口时触发压缩,此区域一般不实际占用",
|
|
22
22
|
"stats.title": "上下文统计",
|
|
23
|
-
"stats.hint": "轮次、步骤与上下文事件",
|
|
24
23
|
"stats.turns": "轮次",
|
|
25
24
|
"stats.steps": "步数",
|
|
26
25
|
"stats.injects": "注入",
|
|
@@ -35,7 +34,6 @@ window.__ModuleLoader__.load({
|
|
|
35
34
|
"stats.costMiss": "未命中",
|
|
36
35
|
"stats.costOut": "输出",
|
|
37
36
|
"timing.title": "耗时统计",
|
|
38
|
-
"timing.hint": "整个会话的活跃时长分布",
|
|
39
37
|
"timing.total": "总活跃时长",
|
|
40
38
|
"timing.ttft": "模型等待",
|
|
41
39
|
"timing.gen": "模型生成",
|
|
@@ -45,7 +43,6 @@ window.__ModuleLoader__.load({
|
|
|
45
43
|
"timing.toolTimes": "{n}次",
|
|
46
44
|
"timing.empty": "暂无耗时数据 · 对话开始后这里会显示时长分布",
|
|
47
45
|
"tokens.title": "Token 统计",
|
|
48
|
-
"tokens.hint": "供应商上报的累计计费用量",
|
|
49
46
|
"tokens.cacheHit": "缓存命中",
|
|
50
47
|
"tokens.cacheRead": "缓存输入",
|
|
51
48
|
"tokens.cacheWrite": "缓存写入",
|
|
@@ -188,6 +185,8 @@ window.__ModuleLoader__.load({
|
|
|
188
185
|
"agents.billed": "累计计费 {n}",
|
|
189
186
|
"agents.open": "点击打开会话 →",
|
|
190
187
|
"loading": "正在读取会话日志…",
|
|
188
|
+
"detail.loading": "正在加载历史数据…",
|
|
189
|
+
"detail.loadFailed": "历史数据加载失败,点击重试",
|
|
191
190
|
"error": "上下文数据读取失败:",
|
|
192
191
|
"error.retry": "重试",
|
|
193
192
|
"gate.title": "请更新 DeepSeek Harness 版本",
|
|
@@ -303,7 +302,6 @@ window.__ModuleLoader__.load({
|
|
|
303
302
|
"overview.ofUsed": "of used context",
|
|
304
303
|
"overview.compactReserve": "Auto-compaction reserve: auto-compaction triggers at {pct}% of the window — this area is normally kept as headroom, not actually used",
|
|
305
304
|
"stats.title": "Context Stats",
|
|
306
|
-
"stats.hint": "Turns, steps, and context events",
|
|
307
305
|
"stats.turns": "Turns",
|
|
308
306
|
"stats.steps": "Steps",
|
|
309
307
|
"stats.injects": "Injections",
|
|
@@ -318,7 +316,6 @@ window.__ModuleLoader__.load({
|
|
|
318
316
|
"stats.costMiss": "miss",
|
|
319
317
|
"stats.costOut": "output",
|
|
320
318
|
"timing.title": "Timing Stats",
|
|
321
|
-
"timing.hint": "Whole-session active-time split",
|
|
322
319
|
"timing.total": "Active Time",
|
|
323
320
|
"timing.ttft": "TTFT",
|
|
324
321
|
"timing.gen": "LLM Gen",
|
|
@@ -328,7 +325,6 @@ window.__ModuleLoader__.load({
|
|
|
328
325
|
"timing.toolTimes": "{n} runs",
|
|
329
326
|
"timing.empty": "No timing data yet — durations appear as the conversation runs",
|
|
330
327
|
"tokens.title": "Token Stats",
|
|
331
|
-
"tokens.hint": "Cumulative provider-reported usage",
|
|
332
328
|
"tokens.cacheHit": "Cache Hit",
|
|
333
329
|
"tokens.cacheRead": "Cached Input",
|
|
334
330
|
"tokens.cacheWrite": "Cache Write",
|
|
@@ -471,6 +467,8 @@ window.__ModuleLoader__.load({
|
|
|
471
467
|
"agents.billed": "{n} billed",
|
|
472
468
|
"agents.open": "click to open →",
|
|
473
469
|
"loading": "Reading the session log…",
|
|
470
|
+
"detail.loading": "Loading history…",
|
|
471
|
+
"detail.loadFailed": "History failed to load — click to retry",
|
|
474
472
|
"error": "Failed to read context data: ",
|
|
475
473
|
"error.retry": "Retry",
|
|
476
474
|
"gate.title": "Update DeepSeek Harness",
|
|
@@ -573,9 +571,9 @@ window.__ModuleLoader__.load({
|
|
|
573
571
|
};
|
|
574
572
|
//#endregion
|
|
575
573
|
//#region src/client/modalStore.ts
|
|
576
|
-
const stores = /* @__PURE__ */ new Map();
|
|
574
|
+
const stores$1 = /* @__PURE__ */ new Map();
|
|
577
575
|
function modalStoreOf(sessionId) {
|
|
578
|
-
const existing = stores.get(sessionId);
|
|
576
|
+
const existing = stores$1.get(sessionId);
|
|
579
577
|
if (existing !== void 0) return existing;
|
|
580
578
|
let open = false;
|
|
581
579
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -593,7 +591,7 @@ window.__ModuleLoader__.load({
|
|
|
593
591
|
for (const listener of listeners) listener();
|
|
594
592
|
}
|
|
595
593
|
};
|
|
596
|
-
stores.set(sessionId, store);
|
|
594
|
+
stores$1.set(sessionId, store);
|
|
597
595
|
return store;
|
|
598
596
|
}
|
|
599
597
|
const pendingConsume = /* @__PURE__ */ new Map();
|
|
@@ -716,7 +714,6 @@ window.__ModuleLoader__.load({
|
|
|
716
714
|
"assistant",
|
|
717
715
|
"tool"
|
|
718
716
|
];
|
|
719
|
-
Object.fromEntries(CATS.map((c) => [c.key, c.color]));
|
|
720
717
|
function partsOf(breakdown) {
|
|
721
718
|
return CATS.map((c) => {
|
|
722
719
|
return {
|
|
@@ -796,9 +793,11 @@ window.__ModuleLoader__.load({
|
|
|
796
793
|
function headlineOf(data, pressure = null, breakdown = null) {
|
|
797
794
|
const current = data.current;
|
|
798
795
|
const projected = pressure !== null && typeof pressure.projectedTokens === "number" ? pressure.projectedTokens : void 0;
|
|
796
|
+
const last = data.last;
|
|
799
797
|
const requests = data.requests;
|
|
800
798
|
const lastReq = requests.length > 0 ? requests[requests.length - 1] : null;
|
|
801
|
-
const
|
|
799
|
+
const anchor = last !== void 0 ? last : lastReq;
|
|
800
|
+
const derived = anchor !== null && typeof anchor.prompt === "number" ? anchor.prompt + (current.total - anchor.total) : void 0;
|
|
802
801
|
const occupancyTokens = projected ?? derived ?? null;
|
|
803
802
|
const window = pressure !== null && typeof pressure.contextWindow === "number" ? pressure.contextWindow : data.contextWindow;
|
|
804
803
|
const tokens = occupancyTokens ?? current.total;
|
|
@@ -860,12 +859,25 @@ window.__ModuleLoader__.load({
|
|
|
860
859
|
} catch {}
|
|
861
860
|
}
|
|
862
861
|
/**
|
|
862
|
+
* Read one projection key through the standard seat, narrowed at the
|
|
863
|
+
* boundary: null when the seat is absent (a harness without the projection
|
|
864
|
+
* pipeline) or the delivered value fails the narrow. The seat is a real
|
|
865
|
+
* hook — call this unconditionally at the top of the component, one call
|
|
866
|
+
* per key, in a stable order.
|
|
867
|
+
*/
|
|
868
|
+
function projectionOf(props, key, narrow) {
|
|
869
|
+
if (typeof props.useProjection !== "function") return null;
|
|
870
|
+
return narrow(props.useProjection(key));
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
863
873
|
* Narrow an unknown projection value to a string-keyed record, or null when
|
|
864
874
|
* it is not one. The boundary type is Record<string, unknown> on purpose:
|
|
865
875
|
* every field read below must re-prove itself (the no-white-screen
|
|
866
876
|
* guarantee), so no field may borrow the wire type before its check.
|
|
877
|
+
* Shared by every sanitizer here and by the agent-tree derivation
|
|
878
|
+
* (agentTree.ts) — the ONE record guard for the whole client half.
|
|
867
879
|
*/
|
|
868
|
-
function asRecord
|
|
880
|
+
function asRecord(value) {
|
|
869
881
|
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
870
882
|
return value;
|
|
871
883
|
}
|
|
@@ -876,17 +888,27 @@ window.__ModuleLoader__.load({
|
|
|
876
888
|
function numOf(value) {
|
|
877
889
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
878
890
|
}
|
|
891
|
+
/** Shared per-item collection guard: drop non-object entries, keep the rest. */
|
|
879
892
|
function objectsOf(value) {
|
|
880
893
|
if (!Array.isArray(value)) return [];
|
|
881
894
|
return value.filter((v) => v !== null && typeof v === "object");
|
|
882
895
|
}
|
|
883
896
|
/**
|
|
897
|
+
* The fast path's collection check: a real array whose entries are ALL
|
|
898
|
+
* records. A null/primitive entry would pass a bare Array.isArray yet throw
|
|
899
|
+
* on the first property read downstream (`req.seq` on null), so it sends the
|
|
900
|
+
* value down the sanitizing slow path, where `objectsOf` drops it.
|
|
901
|
+
*/
|
|
902
|
+
function recordsOnly(value) {
|
|
903
|
+
return Array.isArray(value) && value.every((e) => e !== null && typeof e === "object");
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
884
906
|
* Narrow a delivered `unsupported` gate record (the host's baseline gate —
|
|
885
907
|
* see host/fallback.ts): both version strings re-proved, anything else
|
|
886
908
|
* degrades to null (no gate shown) instead of rendering garbage.
|
|
887
909
|
*/
|
|
888
910
|
function unsupportedOf(value) {
|
|
889
|
-
const data = asRecord
|
|
911
|
+
const data = asRecord(value);
|
|
890
912
|
if (data === null) return null;
|
|
891
913
|
if (typeof data.current !== "string" || typeof data.minimum !== "string") return null;
|
|
892
914
|
return {
|
|
@@ -908,7 +930,7 @@ window.__ModuleLoader__.load({
|
|
|
908
930
|
* during render and unmounting the conversation view.
|
|
909
931
|
*/
|
|
910
932
|
function timelineOf(value) {
|
|
911
|
-
const data = asRecord
|
|
933
|
+
const data = asRecord(value);
|
|
912
934
|
if (data === null) return null;
|
|
913
935
|
const current = data.current;
|
|
914
936
|
if (current !== null && typeof current === "object" && [
|
|
@@ -919,11 +941,13 @@ window.__ModuleLoader__.load({
|
|
|
919
941
|
"assistant",
|
|
920
942
|
"tool",
|
|
921
943
|
"total"
|
|
922
|
-
].every((k) => typeof current[k] === "number") &&
|
|
944
|
+
].every((k) => typeof current[k] === "number") && recordsOnly(data.requests) && recordsOnly(data.events) && recordsOnly(data.nodes) && recordsOnly(data.archive) && timingFastOk(data.timing)) return data;
|
|
923
945
|
const safeCurrent = current !== null && typeof current === "object" ? current : {};
|
|
924
946
|
const cost = typeof data.cost === "object" && data.cost !== null && !Array.isArray(data.cost) ? data.cost : void 0;
|
|
925
947
|
const timing = timingOf(data.timing);
|
|
926
948
|
const unsupported = unsupportedOf(data.unsupported);
|
|
949
|
+
const counts = countsOf(data.counts);
|
|
950
|
+
const last = lastOf(data.last);
|
|
927
951
|
return {
|
|
928
952
|
ok: true,
|
|
929
953
|
...unsupported !== null ? { unsupported } : {},
|
|
@@ -946,10 +970,44 @@ window.__ModuleLoader__.load({
|
|
|
946
970
|
...typeof data.images === "number" ? { images: data.images } : {},
|
|
947
971
|
...typeof data.toolCalls === "number" ? { toolCalls: data.toolCalls } : {},
|
|
948
972
|
archive: objectsOf(data.archive),
|
|
973
|
+
...counts !== void 0 ? { counts } : {},
|
|
974
|
+
...last !== void 0 ? { last } : {},
|
|
975
|
+
...typeof data.detailRev === "number" && Number.isFinite(data.detailRev) ? { detailRev: data.detailRev } : {},
|
|
949
976
|
...cost !== void 0 ? { cost } : {},
|
|
950
977
|
...timing !== null ? { timing } : {},
|
|
951
978
|
...typeof data.surfaceFloor === "number" ? { surfaceFloor: data.surfaceFloor } : {},
|
|
952
|
-
...typeof data.archiveFloor === "number" ? { archiveFloor: data.archiveFloor } : {}
|
|
979
|
+
...typeof data.archiveFloor === "number" ? { archiveFloor: data.archiveFloor } : {},
|
|
980
|
+
...data.fileOps !== void 0 ? { fileOps: objectsOf(data.fileOps) } : {},
|
|
981
|
+
...typeof data.fileOpsFloor === "number" ? { fileOpsFloor: data.fileOpsFloor } : {}
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* The split head's count figures, re-proved field by field: a present-but-
|
|
986
|
+
* partial record zeroes its unreadable fields (the stats board's no-NaN
|
|
987
|
+
* guarantee), an absent or non-record value stays absent (legacy generation
|
|
988
|
+
* — callers derive the counts from the collections instead).
|
|
989
|
+
*/
|
|
990
|
+
function countsOf(value) {
|
|
991
|
+
const data = asRecord(value);
|
|
992
|
+
if (data === null) return void 0;
|
|
993
|
+
return {
|
|
994
|
+
turns: numOf(data.turns),
|
|
995
|
+
steps: numOf(data.steps),
|
|
996
|
+
injects: numOf(data.injects),
|
|
997
|
+
compactions: numOf(data.compactions),
|
|
998
|
+
prunes: numOf(data.prunes)
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
/** The split head's newest-request summary; absent or shapeless stays absent. */
|
|
1002
|
+
function lastOf(value) {
|
|
1003
|
+
const data = asRecord(value);
|
|
1004
|
+
if (data === null) return void 0;
|
|
1005
|
+
if (typeof data.seq !== "number" || !Number.isFinite(data.seq)) return void 0;
|
|
1006
|
+
if (typeof data.total !== "number" || !Number.isFinite(data.total)) return void 0;
|
|
1007
|
+
return {
|
|
1008
|
+
seq: data.seq,
|
|
1009
|
+
total: data.total,
|
|
1010
|
+
...typeof data.prompt === "number" && Number.isFinite(data.prompt) ? { prompt: data.prompt } : {}
|
|
953
1011
|
};
|
|
954
1012
|
}
|
|
955
1013
|
/**
|
|
@@ -957,10 +1015,19 @@ window.__ModuleLoader__.load({
|
|
|
957
1015
|
* `contextPressure` projection (provider-anchored occupancy of the next
|
|
958
1016
|
* request). Absent key or value = the meter's projection is not composed
|
|
959
1017
|
* (e.g. a harness without the session-projection registry) — callers fall
|
|
960
|
-
* back to their derived anchor, so the UI degrades gracefully.
|
|
1018
|
+
* back to their derived anchor, so the UI degrades gracefully. The three
|
|
1019
|
+
* fields are independent last-wins records on the wire (dsh's strict wire
|
|
1020
|
+
* schema), so each is re-proved on its own: a wrong-typed field drops out,
|
|
1021
|
+
* the readable ones survive.
|
|
961
1022
|
*/
|
|
962
1023
|
function contextPressureOf(value) {
|
|
963
|
-
|
|
1024
|
+
const data = asRecord(value);
|
|
1025
|
+
if (data === null) return null;
|
|
1026
|
+
const out = {};
|
|
1027
|
+
if (typeof data.pressureTokens === "number" && Number.isFinite(data.pressureTokens)) out.pressureTokens = data.pressureTokens;
|
|
1028
|
+
if (typeof data.projectedTokens === "number" && Number.isFinite(data.projectedTokens)) out.projectedTokens = data.projectedTokens;
|
|
1029
|
+
if (typeof data.contextWindow === "number" && Number.isFinite(data.contextWindow)) out.contextWindow = data.contextWindow;
|
|
1030
|
+
return out;
|
|
964
1031
|
}
|
|
965
1032
|
/**
|
|
966
1033
|
* Narrow a delivered projection value to the official token-meter
|
|
@@ -970,7 +1037,7 @@ window.__ModuleLoader__.load({
|
|
|
970
1037
|
* own sums instead of mixing sources.
|
|
971
1038
|
*/
|
|
972
1039
|
function contextBreakdownOf(value) {
|
|
973
|
-
const data = asRecord
|
|
1040
|
+
const data = asRecord(value);
|
|
974
1041
|
if (data === null) return null;
|
|
975
1042
|
const { systemTokens, toolsTokens, messageTokens } = data;
|
|
976
1043
|
if (typeof systemTokens !== "number" || !Number.isFinite(systemTokens)) return null;
|
|
@@ -986,10 +1053,25 @@ window.__ModuleLoader__.load({
|
|
|
986
1053
|
* Narrow a delivered projection value to the official token-meter
|
|
987
1054
|
* `tokenUsage` projection (durable cumulative provider usage). Absent key or
|
|
988
1055
|
* value = the meter's projection is not composed (or no request has reported
|
|
989
|
-
* usage yet) — callers drop the cache-hit cell to a dash.
|
|
1056
|
+
* usage yet) — callers drop the cache-hit cell to a dash. The wire schema is
|
|
1057
|
+
* strict with all four buckets REQUIRED (dsh token-meter's projectionSchema),
|
|
1058
|
+
* so a partial/corrupt value degrades the whole value to null instead of
|
|
1059
|
+
* undercounting the billed total.
|
|
990
1060
|
*/
|
|
991
1061
|
function tokenUsageOf(value) {
|
|
992
|
-
|
|
1062
|
+
const data = asRecord(value);
|
|
1063
|
+
if (data === null) return null;
|
|
1064
|
+
const { uncachedInputTokens, outputTokens, cacheReadTokens, cacheWriteTokens } = data;
|
|
1065
|
+
if (typeof uncachedInputTokens !== "number" || !Number.isFinite(uncachedInputTokens)) return null;
|
|
1066
|
+
if (typeof outputTokens !== "number" || !Number.isFinite(outputTokens)) return null;
|
|
1067
|
+
if (typeof cacheReadTokens !== "number" || !Number.isFinite(cacheReadTokens)) return null;
|
|
1068
|
+
if (typeof cacheWriteTokens !== "number" || !Number.isFinite(cacheWriteTokens)) return null;
|
|
1069
|
+
return {
|
|
1070
|
+
uncachedInputTokens,
|
|
1071
|
+
outputTokens,
|
|
1072
|
+
cacheReadTokens,
|
|
1073
|
+
cacheWriteTokens
|
|
1074
|
+
};
|
|
993
1075
|
}
|
|
994
1076
|
/**
|
|
995
1077
|
* A non-negative finite number (the timing totals' every field): NaN or a
|
|
@@ -1034,7 +1116,7 @@ window.__ModuleLoader__.load({
|
|
|
1034
1116
|
* hostile row never blanks the ranking.
|
|
1035
1117
|
*/
|
|
1036
1118
|
function timingOf(value) {
|
|
1037
|
-
const data = asRecord
|
|
1119
|
+
const data = asRecord(value);
|
|
1038
1120
|
if (data === null) return null;
|
|
1039
1121
|
const tools = {};
|
|
1040
1122
|
const rawTools = data.tools;
|
|
@@ -1067,9 +1149,11 @@ window.__ModuleLoader__.load({
|
|
|
1067
1149
|
* browser degrades its system/tools sections to a metadata-only note.
|
|
1068
1150
|
*
|
|
1069
1151
|
* Entry-level shape is checked too: a malformed epoch (corrupt payload with
|
|
1070
|
-
* a missing tools list
|
|
1071
|
-
*
|
|
1072
|
-
*
|
|
1152
|
+
* a missing tools list, a wrong-typed systemTokens, or a tool row whose
|
|
1153
|
+
* name/tokens the browser reads blindly — `tool.name.toLowerCase()` and
|
|
1154
|
+
* `b.tokens - a.tokens` throw on junk) would crash the browser's
|
|
1155
|
+
* tools/sections reads, so the WHOLE projection degrades to null and the
|
|
1156
|
+
* card falls back to its metadata-only note. The epoch CONTENT is
|
|
1073
1157
|
* not part of this value — the browser fetches it per epoch on demand.
|
|
1074
1158
|
*
|
|
1075
1159
|
* The pre-#37 wire generation carries the system TEXT instead of its token
|
|
@@ -1080,13 +1164,20 @@ window.__ModuleLoader__.load({
|
|
|
1080
1164
|
* new-shape values pass through untouched.
|
|
1081
1165
|
*/
|
|
1082
1166
|
function headersOf(value) {
|
|
1083
|
-
const headers = asRecord
|
|
1167
|
+
const headers = asRecord(value);
|
|
1084
1168
|
if (headers === null || !Array.isArray(headers.headers)) return null;
|
|
1085
1169
|
for (const h of headers.headers) {
|
|
1086
1170
|
if (h === null || typeof h !== "object") return null;
|
|
1087
1171
|
const entry = h;
|
|
1088
1172
|
if (!Array.isArray(entry.tools)) return null;
|
|
1089
1173
|
if (entry.systemTokens !== void 0 && (typeof entry.systemTokens !== "number" || !Number.isFinite(entry.systemTokens))) return null;
|
|
1174
|
+
for (const t of entry.tools) {
|
|
1175
|
+
if (t === null || typeof t !== "object") return null;
|
|
1176
|
+
const tool = t;
|
|
1177
|
+
if (typeof tool.name !== "string") return null;
|
|
1178
|
+
if (typeof tool.tokens !== "number" || !Number.isFinite(tool.tokens)) return null;
|
|
1179
|
+
if (tool.plugin !== void 0 && typeof tool.plugin !== "string") return null;
|
|
1180
|
+
}
|
|
1090
1181
|
}
|
|
1091
1182
|
let legacy = false;
|
|
1092
1183
|
for (const entry of headers.headers) if (entry.systemTokens === void 0 && typeof entry.system === "string" && entry.system !== "") {
|
|
@@ -1102,6 +1193,21 @@ window.__ModuleLoader__.load({
|
|
|
1102
1193
|
};
|
|
1103
1194
|
}) };
|
|
1104
1195
|
}
|
|
1196
|
+
/** The session-namespace workspace-opener remotes, ridden through the generic '/api' channel. */
|
|
1197
|
+
const OPEN_CHANNEL = "/api";
|
|
1198
|
+
const CAN_OPEN_ENDPOINT = "session/canOpenWorkspacePath";
|
|
1199
|
+
const OPEN_ENDPOINT = "session/openWorkspacePath";
|
|
1200
|
+
/**
|
|
1201
|
+
* The connection's bound generic-RPC caller, or undefined when the service
|
|
1202
|
+
* is absent or hostile — every read is guarded, so this can never throw.
|
|
1203
|
+
*/
|
|
1204
|
+
function rpcCallOf(ctx) {
|
|
1205
|
+
try {
|
|
1206
|
+
const rpc = asRecord(ctx.get("connection")?.rpc);
|
|
1207
|
+
const fn = rpc?.call;
|
|
1208
|
+
if (rpc !== null && typeof fn === "function") return fn.bind(rpc);
|
|
1209
|
+
} catch {}
|
|
1210
|
+
}
|
|
1105
1211
|
/**
|
|
1106
1212
|
* The SESSION's workspace root — the `cwd` its session-list row carries (the
|
|
1107
1213
|
* host session canon, not the host process's own launch directory) — or
|
|
@@ -1121,29 +1227,37 @@ window.__ModuleLoader__.load({
|
|
|
1121
1227
|
return;
|
|
1122
1228
|
}
|
|
1123
1229
|
}
|
|
1124
|
-
/**
|
|
1125
|
-
*
|
|
1126
|
-
|
|
1230
|
+
/**
|
|
1231
|
+
* Whether this deployment can hand a path to the user's native desktop: the
|
|
1232
|
+
* page must reach the Host on the operator's own machine (`isLoopback`, the
|
|
1233
|
+
* harness's own gate) AND the session controller's opener capability remote
|
|
1234
|
+
* must answer true. The capability is an RPC round-trip now (the synchronous
|
|
1235
|
+
* host-description fact is gone), so the answer is asynchronous; every
|
|
1236
|
+
* absence, hostility, or transport failure resolves false — never a rejection.
|
|
1237
|
+
*/
|
|
1238
|
+
async function canOpenPathsOf(ctx) {
|
|
1239
|
+
const call = rpcCallOf(ctx);
|
|
1240
|
+
if (call === void 0) return false;
|
|
1127
1241
|
try {
|
|
1128
|
-
|
|
1129
|
-
const
|
|
1130
|
-
return
|
|
1242
|
+
if (ctx.get("connection")?.isLoopback !== true) return false;
|
|
1243
|
+
const r = asRecord(await call(OPEN_CHANNEL, CAN_OPEN_ENDPOINT, { args: {} }));
|
|
1244
|
+
return r !== null && r.ok === true && r.value === true;
|
|
1131
1245
|
} catch {
|
|
1132
1246
|
return false;
|
|
1133
1247
|
}
|
|
1134
1248
|
}
|
|
1135
1249
|
/**
|
|
1136
|
-
* The system path opener
|
|
1137
|
-
*
|
|
1138
|
-
*
|
|
1250
|
+
* The system path opener over the session controller's open remote, or
|
|
1251
|
+
* undefined when the connection carries no RPC caller. Fire-and-forget:
|
|
1252
|
+
* rejections (unknown path, no desktop, offline) swallow — the affordance
|
|
1253
|
+
* is best-effort by nature.
|
|
1139
1254
|
*/
|
|
1140
1255
|
function openPathVia(ctx) {
|
|
1141
|
-
const
|
|
1142
|
-
if (
|
|
1143
|
-
const openPath = host.openPath.bind(host);
|
|
1256
|
+
const call = rpcCallOf(ctx);
|
|
1257
|
+
if (call === void 0) return void 0;
|
|
1144
1258
|
return (path) => {
|
|
1145
1259
|
try {
|
|
1146
|
-
|
|
1260
|
+
call(OPEN_CHANNEL, OPEN_ENDPOINT, { args: { request: { path } } }).catch(() => {});
|
|
1147
1261
|
} catch {}
|
|
1148
1262
|
};
|
|
1149
1263
|
}
|
|
@@ -1428,6 +1542,244 @@ window.__ModuleLoader__.load({
|
|
|
1428
1542
|
};
|
|
1429
1543
|
}
|
|
1430
1544
|
//#endregion
|
|
1545
|
+
//#region src/client/react.ts
|
|
1546
|
+
const React = require("react");
|
|
1547
|
+
const h = React.createElement;
|
|
1548
|
+
const ReactDOM = require("react-dom");
|
|
1549
|
+
//#endregion
|
|
1550
|
+
//#region src/client/timelineSource.ts
|
|
1551
|
+
const DETAIL_CHANNEL = "/dsh-context";
|
|
1552
|
+
const DETAIL_ENDPOINT = "detail";
|
|
1553
|
+
/**
|
|
1554
|
+
* Narrow the detail endpoint's payload to a render-safe value (the same
|
|
1555
|
+
* boundary rigor as `timelineOf`): collections re-proved per item, scalars
|
|
1556
|
+
* zeroed, a missing/NaN revision rejects the whole payload (the caller then
|
|
1557
|
+
* shows the retryable failure note instead of half-merged data).
|
|
1558
|
+
*/
|
|
1559
|
+
function detailOf(value) {
|
|
1560
|
+
const data = asRecord(value);
|
|
1561
|
+
if (data === null) return null;
|
|
1562
|
+
if (typeof data.rev !== "number" || !Number.isFinite(data.rev) || data.rev < 0) return null;
|
|
1563
|
+
return {
|
|
1564
|
+
rev: data.rev,
|
|
1565
|
+
requests: objectsOf(data.requests),
|
|
1566
|
+
events: objectsOf(data.events),
|
|
1567
|
+
nodes: objectsOf(data.nodes),
|
|
1568
|
+
droppedNodes: numOf(data.droppedNodes),
|
|
1569
|
+
archive: objectsOf(data.archive),
|
|
1570
|
+
...typeof data.surfaceFloor === "number" ? { surfaceFloor: data.surfaceFloor } : {},
|
|
1571
|
+
...typeof data.archiveFloor === "number" ? { archiveFloor: data.archiveFloor } : {},
|
|
1572
|
+
...data.fileOps !== void 0 ? { fileOps: objectsOf(data.fileOps) } : {},
|
|
1573
|
+
...typeof data.fileOpsFloor === "number" ? { fileOpsFloor: data.fileOpsFloor } : {}
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* The detail reader over the harness's generic Connection RPC
|
|
1578
|
+
* (`ctx.connection.rpc.call`) — resolved through the shared `rpcCallOf`
|
|
1579
|
+
* reflect read (the same seam openPathVia/canOpenPathsOf use), so a hostile
|
|
1580
|
+
* or absent connection service degrades to `undefined` instead of throwing.
|
|
1581
|
+
* The returned thunk resolves the session's current detail, `null` when the
|
|
1582
|
+
* session is not live anymore, and rejects on transport failure or a
|
|
1583
|
+
* malformed payload (the store turns the two into the retryable state).
|
|
1584
|
+
*/
|
|
1585
|
+
function makeDetailFetcher(ctx, sessionId) {
|
|
1586
|
+
if (sessionId === "") return void 0;
|
|
1587
|
+
const call = rpcCallOf(ctx);
|
|
1588
|
+
if (call === void 0) return void 0;
|
|
1589
|
+
return async () => {
|
|
1590
|
+
const r = asRecord(await call(DETAIL_CHANNEL, DETAIL_ENDPOINT, { sessionId }));
|
|
1591
|
+
if (r === null || r.ok !== true) throw new Error("dsh-context: detail rpc failed");
|
|
1592
|
+
if (r.value === null) return null;
|
|
1593
|
+
const detail = detailOf(r.value);
|
|
1594
|
+
if (detail === null) throw new Error("dsh-context: detail rpc malformed");
|
|
1595
|
+
return detail;
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
const EMPTY_SNAP = {
|
|
1599
|
+
detail: null,
|
|
1600
|
+
failed: false,
|
|
1601
|
+
pending: false
|
|
1602
|
+
};
|
|
1603
|
+
/** The fetch debounce base; each consecutive failure doubles the wait, capped at 3 doublings. */
|
|
1604
|
+
const DETAIL_DEBOUNCE_MS = 300;
|
|
1605
|
+
/**
|
|
1606
|
+
* One session's detail ledger. Exported for tests (a zero debounce makes the
|
|
1607
|
+
* machine synchronous-ish); the app reaches it through `detailStoreOf`.
|
|
1608
|
+
*/
|
|
1609
|
+
var DetailStore = class {
|
|
1610
|
+
fetcher;
|
|
1611
|
+
baseDelay;
|
|
1612
|
+
detail = null;
|
|
1613
|
+
/** The revision of `detail` (latest-wins cursor); -1 before the first landing. */
|
|
1614
|
+
acceptedRev = -1;
|
|
1615
|
+
/** The newest revision the head has asked for. */
|
|
1616
|
+
wantedRev = -1;
|
|
1617
|
+
/** The head rev the IN-FLIGHT (or scheduled) read targets — the refold exception's acceptance key. */
|
|
1618
|
+
targetRev = -1;
|
|
1619
|
+
/** The head rev seen last — a DECREASE means the host refolded (revisions restart). */
|
|
1620
|
+
lastHeadRev = -1;
|
|
1621
|
+
failed = false;
|
|
1622
|
+
inFlight = false;
|
|
1623
|
+
timer = null;
|
|
1624
|
+
failures = 0;
|
|
1625
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1626
|
+
snap = EMPTY_SNAP;
|
|
1627
|
+
constructor(fetcher, baseDelay = DETAIL_DEBOUNCE_MS) {
|
|
1628
|
+
this.fetcher = fetcher;
|
|
1629
|
+
this.baseDelay = baseDelay;
|
|
1630
|
+
}
|
|
1631
|
+
subscribe = (fn) => {
|
|
1632
|
+
this.listeners.add(fn);
|
|
1633
|
+
return () => this.listeners.delete(fn);
|
|
1634
|
+
};
|
|
1635
|
+
getSnapshot = () => this.snap;
|
|
1636
|
+
/**
|
|
1637
|
+
* The head's current revision arrived: schedule the trailing-edge read
|
|
1638
|
+
* when it outruns the served detail. A rev DECREASE means the host
|
|
1639
|
+
* refolded the session (a discarded checkpoint, a restart): revisions are
|
|
1640
|
+
* no longer comparable, so the ledger resets and refetches.
|
|
1641
|
+
*/
|
|
1642
|
+
request(rev) {
|
|
1643
|
+
if (rev < this.lastHeadRev) {
|
|
1644
|
+
this.acceptedRev = -1;
|
|
1645
|
+
this.wantedRev = -1;
|
|
1646
|
+
this.detail = null;
|
|
1647
|
+
this.failed = false;
|
|
1648
|
+
}
|
|
1649
|
+
this.lastHeadRev = rev;
|
|
1650
|
+
if (rev <= this.acceptedRev || rev <= this.wantedRev) return;
|
|
1651
|
+
this.wantedRev = rev;
|
|
1652
|
+
this.schedule();
|
|
1653
|
+
}
|
|
1654
|
+
/** Re-arm after a failure (the cards' retry note): immediate, backoff reset. */
|
|
1655
|
+
retry = () => {
|
|
1656
|
+
this.failures = 0;
|
|
1657
|
+
if (this.detail !== null) return;
|
|
1658
|
+
if (this.timer !== null) {
|
|
1659
|
+
clearTimeout(this.timer);
|
|
1660
|
+
this.timer = null;
|
|
1661
|
+
}
|
|
1662
|
+
if (!this.inFlight) this.fire();
|
|
1663
|
+
};
|
|
1664
|
+
schedule() {
|
|
1665
|
+
if (this.timer !== null) return;
|
|
1666
|
+
this.timer = setTimeout(() => {
|
|
1667
|
+
this.timer = null;
|
|
1668
|
+
this.fire();
|
|
1669
|
+
}, this.baseDelay * 2 ** Math.min(this.failures, 3));
|
|
1670
|
+
this.emit();
|
|
1671
|
+
}
|
|
1672
|
+
async fire() {
|
|
1673
|
+
if (this.inFlight) return;
|
|
1674
|
+
if (this.fetcher === void 0) {
|
|
1675
|
+
this.failed = true;
|
|
1676
|
+
this.emit();
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1679
|
+
this.inFlight = true;
|
|
1680
|
+
this.targetRev = this.wantedRev;
|
|
1681
|
+
this.emit();
|
|
1682
|
+
try {
|
|
1683
|
+
const d = await this.fetcher();
|
|
1684
|
+
if (d !== null) {
|
|
1685
|
+
if (d.rev >= this.acceptedRev || d.rev === this.targetRev) {
|
|
1686
|
+
this.detail = d;
|
|
1687
|
+
this.acceptedRev = d.rev;
|
|
1688
|
+
}
|
|
1689
|
+
this.failures = 0;
|
|
1690
|
+
this.failed = false;
|
|
1691
|
+
} else {
|
|
1692
|
+
this.wantedRev = this.acceptedRev;
|
|
1693
|
+
this.failed = this.detail === null;
|
|
1694
|
+
this.failures++;
|
|
1695
|
+
}
|
|
1696
|
+
} catch {
|
|
1697
|
+
this.failed = this.detail === null;
|
|
1698
|
+
this.failures++;
|
|
1699
|
+
}
|
|
1700
|
+
this.inFlight = false;
|
|
1701
|
+
if (this.wantedRev > this.acceptedRev) this.schedule();
|
|
1702
|
+
this.emit();
|
|
1703
|
+
}
|
|
1704
|
+
emit() {
|
|
1705
|
+
const pending = this.timer !== null || this.inFlight;
|
|
1706
|
+
const next = {
|
|
1707
|
+
detail: this.detail,
|
|
1708
|
+
failed: this.failed,
|
|
1709
|
+
pending
|
|
1710
|
+
};
|
|
1711
|
+
if (next.detail === this.snap.detail && next.failed === this.snap.failed && next.pending === this.snap.pending) return;
|
|
1712
|
+
this.snap = next;
|
|
1713
|
+
for (const fn of this.listeners) fn();
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
/** Page-lifetime per-session stores (the tab and the modal share one). */
|
|
1717
|
+
const stores = /* @__PURE__ */ new Map();
|
|
1718
|
+
function detailStoreOf(ctx, sessionId) {
|
|
1719
|
+
let store = stores.get(sessionId);
|
|
1720
|
+
if (store === void 0) {
|
|
1721
|
+
store = new DetailStore(makeDetailFetcher(ctx, sessionId));
|
|
1722
|
+
stores.set(sessionId, store);
|
|
1723
|
+
}
|
|
1724
|
+
return store;
|
|
1725
|
+
}
|
|
1726
|
+
const noopSubscribe = () => () => {};
|
|
1727
|
+
const noopRetry = () => {};
|
|
1728
|
+
/**
|
|
1729
|
+
* The view's one read of the timeline (see the module header for the
|
|
1730
|
+
* generation rules). Hook-order safe: every hook runs unconditionally, the
|
|
1731
|
+
* branches below only shape the returned record.
|
|
1732
|
+
*/
|
|
1733
|
+
function useTimelineSource(ctx, props) {
|
|
1734
|
+
const head = projectionOf(props, "contextTimeline", timelineOf);
|
|
1735
|
+
const sessionId = typeof props.sessionId === "string" ? props.sessionId : "";
|
|
1736
|
+
const headRev = head !== null && typeof head.detailRev === "number" ? head.detailRev : null;
|
|
1737
|
+
const slim = headRev !== null;
|
|
1738
|
+
const store = React.useMemo(() => slim ? detailStoreOf(ctx, sessionId) : null, [
|
|
1739
|
+
ctx,
|
|
1740
|
+
sessionId,
|
|
1741
|
+
slim
|
|
1742
|
+
]);
|
|
1743
|
+
const snap = React.useSyncExternalStore(store !== null ? store.subscribe : noopSubscribe, store !== null ? store.getSnapshot : () => EMPTY_SNAP);
|
|
1744
|
+
React.useEffect(() => {
|
|
1745
|
+
if (store !== null && headRev !== null) store.request(headRev);
|
|
1746
|
+
}, [store, headRev]);
|
|
1747
|
+
return React.useMemo(() => {
|
|
1748
|
+
if (head === null) return {
|
|
1749
|
+
data: null,
|
|
1750
|
+
detailState: "loading",
|
|
1751
|
+
retryDetail: noopRetry
|
|
1752
|
+
};
|
|
1753
|
+
if (!slim || store === null) return {
|
|
1754
|
+
data: head,
|
|
1755
|
+
detailState: "legacy",
|
|
1756
|
+
retryDetail: noopRetry
|
|
1757
|
+
};
|
|
1758
|
+
const detail = snap.detail;
|
|
1759
|
+
return {
|
|
1760
|
+
data: detail === null ? head : {
|
|
1761
|
+
...head,
|
|
1762
|
+
requests: detail.requests,
|
|
1763
|
+
events: detail.events,
|
|
1764
|
+
nodes: detail.nodes,
|
|
1765
|
+
droppedNodes: detail.droppedNodes,
|
|
1766
|
+
archive: detail.archive,
|
|
1767
|
+
...detail.surfaceFloor !== void 0 ? { surfaceFloor: detail.surfaceFloor } : {},
|
|
1768
|
+
...detail.archiveFloor !== void 0 ? { archiveFloor: detail.archiveFloor } : {},
|
|
1769
|
+
...detail.fileOps !== void 0 ? { fileOps: detail.fileOps } : {},
|
|
1770
|
+
...detail.fileOpsFloor !== void 0 ? { fileOpsFloor: detail.fileOpsFloor } : {}
|
|
1771
|
+
},
|
|
1772
|
+
detailState: detail !== null ? "ready" : snap.failed ? "failed" : "loading",
|
|
1773
|
+
retryDetail: store.retry
|
|
1774
|
+
};
|
|
1775
|
+
}, [
|
|
1776
|
+
head,
|
|
1777
|
+
slim,
|
|
1778
|
+
store,
|
|
1779
|
+
snap
|
|
1780
|
+
]);
|
|
1781
|
+
}
|
|
1782
|
+
//#endregion
|
|
1431
1783
|
//#region src/client/assemble.ts
|
|
1432
1784
|
/** The header epoch in force at `seq` (last logged before it), or the newest. */
|
|
1433
1785
|
function headerAt(headers, seq) {
|
|
@@ -1461,12 +1813,8 @@ window.__ModuleLoader__.load({
|
|
|
1461
1813
|
};
|
|
1462
1814
|
}
|
|
1463
1815
|
//#endregion
|
|
1464
|
-
//#region src/
|
|
1465
|
-
|
|
1466
|
-
const h = React.createElement;
|
|
1467
|
-
const ReactDOM = require("react-dom");
|
|
1468
|
-
//#endregion
|
|
1469
|
-
//#region src/client/callSummary.ts
|
|
1816
|
+
//#region src/shared/fileOps.ts
|
|
1817
|
+
/** Parse a call's raw JSON arguments; non-string/malformed/non-record inputs yield null. */
|
|
1470
1818
|
function parseCallArgs(raw) {
|
|
1471
1819
|
if (typeof raw !== "string" || raw === "") return null;
|
|
1472
1820
|
try {
|
|
@@ -1476,6 +1824,223 @@ window.__ModuleLoader__.load({
|
|
|
1476
1824
|
return null;
|
|
1477
1825
|
}
|
|
1478
1826
|
}
|
|
1827
|
+
const KIND_BY_TOOL = {
|
|
1828
|
+
read: "read",
|
|
1829
|
+
read_image: "read",
|
|
1830
|
+
write: "write",
|
|
1831
|
+
edit: "write",
|
|
1832
|
+
grep: "search",
|
|
1833
|
+
glob: "search"
|
|
1834
|
+
};
|
|
1835
|
+
/** The file purpose of a tool, or null for non-file tools (bash, web_search…). */
|
|
1836
|
+
function kindOfTool(tool) {
|
|
1837
|
+
if (tool === void 0) return null;
|
|
1838
|
+
return KIND_BY_TOOL[tool] ?? null;
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* The file purpose of one executed call. Like {@link kindOfTool} except for
|
|
1842
|
+
* the one file tool whose purpose follows its arguments: `str_replace_editor`
|
|
1843
|
+
* reads on `view` and writes on every other command (create / str_replace /
|
|
1844
|
+
* insert — an unknown command writes too; the call failed and the row keeps
|
|
1845
|
+
* its error flag).
|
|
1846
|
+
*/
|
|
1847
|
+
function kindOfCall(tool, args) {
|
|
1848
|
+
if (tool === "str_replace_editor") return args !== null && args.command === "view" ? "read" : "write";
|
|
1849
|
+
return kindOfTool(tool);
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* The operation's target path: the path-ish argument of read/write tools;
|
|
1853
|
+
* for searches the narrowing `path`, else the pattern itself (a pathless
|
|
1854
|
+
* grep/glob's target IS the pattern — the workspace-wide search text).
|
|
1855
|
+
*/
|
|
1856
|
+
function pathOfArgs(tool, args) {
|
|
1857
|
+
if (args === null) return null;
|
|
1858
|
+
if (tool === "grep" || tool === "glob") {
|
|
1859
|
+
const p = args.path;
|
|
1860
|
+
if (typeof p === "string" && p !== "") return p;
|
|
1861
|
+
const pattern = args.pattern;
|
|
1862
|
+
return typeof pattern === "string" && pattern !== "" ? pattern : null;
|
|
1863
|
+
}
|
|
1864
|
+
for (const k of [
|
|
1865
|
+
"file_path",
|
|
1866
|
+
"filePath",
|
|
1867
|
+
"path"
|
|
1868
|
+
]) {
|
|
1869
|
+
const v = args[k];
|
|
1870
|
+
if (typeof v === "string" && v !== "") return v;
|
|
1871
|
+
}
|
|
1872
|
+
return null;
|
|
1873
|
+
}
|
|
1874
|
+
/** Rendered line count: '' is 0, a trailing newline closes its own line. */
|
|
1875
|
+
function linesOf(s) {
|
|
1876
|
+
if (s === "") return 0;
|
|
1877
|
+
let n = 0;
|
|
1878
|
+
for (let i = 0; i < s.length; i++) if (s[i] === "\n") n++;
|
|
1879
|
+
return s.endsWith("\n") ? n : n + 1;
|
|
1880
|
+
}
|
|
1881
|
+
/** The added/removed pair of one content-bearing argument set, or zeros. */
|
|
1882
|
+
function pairOf(added, removed) {
|
|
1883
|
+
return {
|
|
1884
|
+
added: typeof added === "string" ? linesOf(added) : 0,
|
|
1885
|
+
removed: typeof removed === "string" ? linesOf(removed) : 0
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* The signed line footprint of one call: an edit removes its old string and
|
|
1890
|
+
* adds its new one; a write adds its content (the pre-existing body, if any,
|
|
1891
|
+
* is unknowable from the arguments — the estimate stays honest about that);
|
|
1892
|
+
* `str_replace_editor` splits the same shapes across its commands. Callers
|
|
1893
|
+
* reach here only with parsed args (a null parse yields no path).
|
|
1894
|
+
*/
|
|
1895
|
+
function deltaOf(tool, args) {
|
|
1896
|
+
if (tool === "edit") return pairOf(args.new_string, args.old_string);
|
|
1897
|
+
if (tool === "write") return pairOf(args.content, void 0);
|
|
1898
|
+
if (tool === "str_replace_editor") {
|
|
1899
|
+
if (args.command === "str_replace") return pairOf(args.new_str, args.old_str);
|
|
1900
|
+
if (args.command === "insert") return pairOf(args.new_str, void 0);
|
|
1901
|
+
if (args.command === "create") return pairOf(args.file_text, void 0);
|
|
1902
|
+
}
|
|
1903
|
+
return {
|
|
1904
|
+
added: 0,
|
|
1905
|
+
removed: 0
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
/** The exact window a read's result meta reports: `offset` plus the retained
|
|
1909
|
+
* `lines` array (the same bounded payload the read card renders from). Null
|
|
1910
|
+
* for a foreign or malformed meta. */
|
|
1911
|
+
function readWindowOf(meta) {
|
|
1912
|
+
if (meta === null || typeof meta !== "object") return null;
|
|
1913
|
+
const m = meta;
|
|
1914
|
+
if (typeof m.path !== "string" || m.path === "") return null;
|
|
1915
|
+
if (typeof m.offset !== "number" || !Number.isFinite(m.offset) || m.offset < 1) return null;
|
|
1916
|
+
if (!Array.isArray(m.lines) || m.lines.length === 0) return null;
|
|
1917
|
+
return {
|
|
1918
|
+
start: m.offset,
|
|
1919
|
+
count: m.lines.length
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
/** The limit estimate: the tool reads up to `limit` lines from `offset`;
|
|
1923
|
+
* absent when the call reads unbounded. */
|
|
1924
|
+
function readEstimateOf(args) {
|
|
1925
|
+
const limit = args.limit;
|
|
1926
|
+
return typeof limit === "number" && Number.isFinite(limit) && limit > 0 ? {
|
|
1927
|
+
count: Math.floor(limit),
|
|
1928
|
+
est: true
|
|
1929
|
+
} : void 0;
|
|
1930
|
+
}
|
|
1931
|
+
/** What a read op shows: the exact window off the result meta, else the
|
|
1932
|
+
* limit estimate, else nothing (an unbounded read names no footprint). */
|
|
1933
|
+
function readOf(meta, args) {
|
|
1934
|
+
const win = readWindowOf(meta);
|
|
1935
|
+
if (win !== null) return {
|
|
1936
|
+
start: win.start,
|
|
1937
|
+
count: win.count
|
|
1938
|
+
};
|
|
1939
|
+
return readEstimateOf(args);
|
|
1940
|
+
}
|
|
1941
|
+
/**
|
|
1942
|
+
* A search op's detail: the pattern, with the include filter appended when
|
|
1943
|
+
* one narrowed the call. A patternless (malformed) search has no detail.
|
|
1944
|
+
*/
|
|
1945
|
+
function searchDetailOf(args) {
|
|
1946
|
+
const pattern = args?.pattern;
|
|
1947
|
+
if (typeof pattern !== "string" || pattern === "") return void 0;
|
|
1948
|
+
const include = args?.include;
|
|
1949
|
+
return typeof include === "string" && include !== "" ? `${pattern} (${include})` : pattern;
|
|
1950
|
+
}
|
|
1951
|
+
/**
|
|
1952
|
+
* The files a search demonstrably reached, read off the result's bounded
|
|
1953
|
+
* presentation meta (grep groups matched lines by file; glob lists paths).
|
|
1954
|
+
* Only the COMPLETE list attributes: a capped search (`truncated`) names a
|
|
1955
|
+
* partial file set, and a malformed meta names none — both fall back to the
|
|
1956
|
+
* call's own target. Each entry carries the reported match count.
|
|
1957
|
+
*/
|
|
1958
|
+
function searchFilesOf(meta) {
|
|
1959
|
+
if (meta === null || typeof meta !== "object") return null;
|
|
1960
|
+
const m = meta;
|
|
1961
|
+
if (m.truncated !== false) return null;
|
|
1962
|
+
const files = [];
|
|
1963
|
+
if (m.shape === "matches" && Array.isArray(m.files)) for (const f of m.files) {
|
|
1964
|
+
if (f === null || typeof f !== "object") continue;
|
|
1965
|
+
const group = f;
|
|
1966
|
+
if (typeof group.path === "string" && group.path !== "" && Array.isArray(group.matches)) files.push({
|
|
1967
|
+
path: group.path,
|
|
1968
|
+
hits: group.matches.length
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
else if (m.shape === "paths" && Array.isArray(m.paths)) {
|
|
1972
|
+
for (const p of m.paths) if (typeof p === "string" && p !== "") files.push({
|
|
1973
|
+
path: p,
|
|
1974
|
+
hits: 0
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
return files.length > 0 ? files : null;
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* The one-shot per-call op assembly, uniform across every producer: the
|
|
1981
|
+
* host's call/result pairing (args off the armed call, meta off the
|
|
1982
|
+
* result), the nested Code-Mode settle (no meta exists on a dispatch — the
|
|
1983
|
+
* read window and per-file search attribution degrade to the argument-only
|
|
1984
|
+
* forms), and the client's inline-generation join fallback. Returns zero to
|
|
1985
|
+
* N records: a search with the complete matched-file meta rows per file;
|
|
1986
|
+
* any other file call rows once; a non-file tool (or a call whose arguments
|
|
1987
|
+
* resolve no target) rows nothing.
|
|
1988
|
+
*/
|
|
1989
|
+
function opsOfCall(input) {
|
|
1990
|
+
const args = parseCallArgs(input.argsRaw);
|
|
1991
|
+
const kind = kindOfCall(input.tool, args);
|
|
1992
|
+
if (kind === null) return [];
|
|
1993
|
+
const stamp = {
|
|
1994
|
+
seq: input.seq,
|
|
1995
|
+
kind,
|
|
1996
|
+
tool: input.tool,
|
|
1997
|
+
err: input.err === true,
|
|
1998
|
+
added: 0,
|
|
1999
|
+
removed: 0,
|
|
2000
|
+
path: "",
|
|
2001
|
+
...input.time !== void 0 ? { time: input.time } : {},
|
|
2002
|
+
...input.gone !== void 0 ? { gone: input.gone } : {},
|
|
2003
|
+
...input.parent !== void 0 ? { parent: input.parent } : {},
|
|
2004
|
+
...input.program !== void 0 ? { program: input.program } : {}
|
|
2005
|
+
};
|
|
2006
|
+
if (kind === "search") {
|
|
2007
|
+
const files = searchFilesOf(input.meta);
|
|
2008
|
+
if (files !== null) {
|
|
2009
|
+
const detail = searchDetailOf(args);
|
|
2010
|
+
const target = args !== null ? pathOfArgs(input.tool, args) : null;
|
|
2011
|
+
const narrowed = args !== null && typeof args.path === "string" && args.path !== "";
|
|
2012
|
+
return [...target !== null && !files.some((f) => f.path === target) ? [{
|
|
2013
|
+
...stamp,
|
|
2014
|
+
path: target,
|
|
2015
|
+
...narrowed && detail !== void 0 ? { detail } : {},
|
|
2016
|
+
...narrowed ? {} : { pattern: true }
|
|
2017
|
+
}] : [], ...files.map((f) => ({
|
|
2018
|
+
...stamp,
|
|
2019
|
+
path: f.path,
|
|
2020
|
+
...detail !== void 0 ? { detail } : {},
|
|
2021
|
+
...f.hits > 0 ? { hits: f.hits } : {}
|
|
2022
|
+
}))];
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
if (args === null) return [];
|
|
2026
|
+
const path = pathOfArgs(input.tool, args);
|
|
2027
|
+
if (path === null) return [];
|
|
2028
|
+
const { added, removed } = deltaOf(input.tool, args);
|
|
2029
|
+
const narrowed = typeof args.path === "string" && args.path !== "";
|
|
2030
|
+
const detail = kind === "search" && narrowed ? searchDetailOf(args) : void 0;
|
|
2031
|
+
const read = kind === "read" ? readOf(input.meta, args) : void 0;
|
|
2032
|
+
return [{
|
|
2033
|
+
...stamp,
|
|
2034
|
+
path,
|
|
2035
|
+
added,
|
|
2036
|
+
removed,
|
|
2037
|
+
...detail !== void 0 ? { detail } : {},
|
|
2038
|
+
...read !== void 0 ? { read } : {},
|
|
2039
|
+
...kind === "search" && !narrowed ? { pattern: true } : {}
|
|
2040
|
+
}];
|
|
2041
|
+
}
|
|
2042
|
+
//#endregion
|
|
2043
|
+
//#region src/client/callSummary.ts
|
|
1479
2044
|
function summaryInArgs(args) {
|
|
1480
2045
|
if (args === null) return null;
|
|
1481
2046
|
for (const k of [
|
|
@@ -1516,6 +2081,20 @@ window.__ModuleLoader__.load({
|
|
|
1516
2081
|
return names;
|
|
1517
2082
|
}
|
|
1518
2083
|
//#endregion
|
|
2084
|
+
//#region src/client/components/detailNote.tsx
|
|
2085
|
+
function makeDetailNote(kit) {
|
|
2086
|
+
const { t } = kit;
|
|
2087
|
+
return function DetailNote(props) {
|
|
2088
|
+
const cls = props.className ?? "lc-empty";
|
|
2089
|
+
if (props.state === "loading") return /* @__PURE__ */ React.createElement("div", { className: cls }, t("detail.loading"));
|
|
2090
|
+
return /* @__PURE__ */ React.createElement("div", { className: cls }, props.onRetry !== void 0 ? /* @__PURE__ */ React.createElement("button", {
|
|
2091
|
+
type: "button",
|
|
2092
|
+
className: "lc-br-retry",
|
|
2093
|
+
onClick: props.onRetry
|
|
2094
|
+
}, t("detail.loadFailed")) : t("detail.loadFailed"));
|
|
2095
|
+
};
|
|
2096
|
+
}
|
|
2097
|
+
//#endregion
|
|
1519
2098
|
//#region src/client/components/nodes.tsx
|
|
1520
2099
|
function makeNodeText(kit) {
|
|
1521
2100
|
const { t } = kit;
|
|
@@ -1530,6 +2109,71 @@ window.__ModuleLoader__.load({
|
|
|
1530
2109
|
};
|
|
1531
2110
|
}
|
|
1532
2111
|
//#endregion
|
|
2112
|
+
//#region src/client/components/fetchOnMiss.tsx
|
|
2113
|
+
/**
|
|
2114
|
+
* Run one fetch for `key` when it is set and not yet cached. `key` null (no
|
|
2115
|
+
* target open) or `fetcher` undefined (older host without the history face)
|
|
2116
|
+
* leaves the machine idle; the caller renders the matching note either way.
|
|
2117
|
+
*/
|
|
2118
|
+
function useFetchOnMiss(key, fetcher, warn) {
|
|
2119
|
+
const [values, setValues] = React.useState(() => /* @__PURE__ */ new Map());
|
|
2120
|
+
const [state, setState] = React.useState("idle");
|
|
2121
|
+
const [attempt, setAttempt] = React.useState(0);
|
|
2122
|
+
React.useEffect(() => {
|
|
2123
|
+
if (key === null || fetcher === void 0 || values.has(key)) return;
|
|
2124
|
+
let live = true;
|
|
2125
|
+
setState("loading");
|
|
2126
|
+
fetcher(key).then((value) => {
|
|
2127
|
+
if (!live) return;
|
|
2128
|
+
if (value === null) {
|
|
2129
|
+
setState("absent");
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
setValues((prev) => {
|
|
2133
|
+
const next = new Map(prev);
|
|
2134
|
+
next.set(key, value);
|
|
2135
|
+
return next;
|
|
2136
|
+
});
|
|
2137
|
+
setState("idle");
|
|
2138
|
+
}, (error) => {
|
|
2139
|
+
console.warn(warn, error);
|
|
2140
|
+
if (live) setState("failed");
|
|
2141
|
+
});
|
|
2142
|
+
return () => {
|
|
2143
|
+
live = false;
|
|
2144
|
+
};
|
|
2145
|
+
}, [
|
|
2146
|
+
key,
|
|
2147
|
+
fetcher,
|
|
2148
|
+
attempt,
|
|
2149
|
+
values
|
|
2150
|
+
]);
|
|
2151
|
+
return {
|
|
2152
|
+
values,
|
|
2153
|
+
state,
|
|
2154
|
+
retry: () => {
|
|
2155
|
+
setAttempt((a) => a + 1);
|
|
2156
|
+
}
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
/**
|
|
2160
|
+
* The note a not-yet-loaded fetch target shows — one expression for both
|
|
2161
|
+
* on-demand reads: the legacy static hint (`emptyKey`) when no fetcher is
|
|
2162
|
+
* composed, `loading` while the read is in flight (and on the first frame
|
|
2163
|
+
* before the effect fires), `notInLog` when the durable log does not hold
|
|
2164
|
+
* the key, and a retry button after a failed read.
|
|
2165
|
+
*/
|
|
2166
|
+
function fetchMissNote(t, fetcher, state, onRetry, emptyKey) {
|
|
2167
|
+
if (fetcher === void 0) return t(emptyKey);
|
|
2168
|
+
if (state === "absent") return t("browser.notInLog");
|
|
2169
|
+
if (state === "failed") return /* @__PURE__ */ React.createElement("button", {
|
|
2170
|
+
type: "button",
|
|
2171
|
+
className: "lc-br-retry",
|
|
2172
|
+
onClick: onRetry
|
|
2173
|
+
}, t("browser.loadFailed"));
|
|
2174
|
+
return t("browser.loading");
|
|
2175
|
+
}
|
|
2176
|
+
//#endregion
|
|
1533
2177
|
//#region src/client/format.ts
|
|
1534
2178
|
/** `fmt`: the k/M suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
|
|
1535
2179
|
function fmt(n) {
|
|
@@ -1583,7 +2227,7 @@ window.__ModuleLoader__.load({
|
|
|
1583
2227
|
* Non-finite or non-positive input shows the dash (callers render their empty
|
|
1584
2228
|
* state anyway).
|
|
1585
2229
|
*/
|
|
1586
|
-
function fmtDuration
|
|
2230
|
+
function fmtDuration(ms, lang) {
|
|
1587
2231
|
if (!Number.isFinite(ms) || ms <= 0) return "—";
|
|
1588
2232
|
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
1589
2233
|
const zh = lang === "zh";
|
|
@@ -2323,6 +2967,7 @@ window.__ModuleLoader__.load({
|
|
|
2323
2967
|
}
|
|
2324
2968
|
function makeContextBrowser(kit, StackedBar) {
|
|
2325
2969
|
const { t, fmt, fmtTime, catLabel } = kit;
|
|
2970
|
+
const DetailNote = makeDetailNote(kit);
|
|
2326
2971
|
const nodeText = makeNodeText(kit);
|
|
2327
2972
|
const rich = makeRichText(kit);
|
|
2328
2973
|
const ImageCard = makeImageCard(kit);
|
|
@@ -2335,46 +2980,20 @@ window.__ModuleLoader__.load({
|
|
|
2335
2980
|
const [rowQuery, setRowQuery] = React.useState("");
|
|
2336
2981
|
const [toolSort, setToolSort] = React.useState("size");
|
|
2337
2982
|
const convNodes = props.convNodes;
|
|
2338
|
-
const
|
|
2339
|
-
const bySeq = React.useMemo(() => {
|
|
2983
|
+
const convBySeq = React.useMemo(() => {
|
|
2340
2984
|
const m = /* @__PURE__ */ new Map();
|
|
2341
2985
|
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
2342
|
-
for (const [seq, n] of fetched) if (!m.has(seq)) m.set(seq, n);
|
|
2343
2986
|
return m;
|
|
2344
|
-
}, [convNodes
|
|
2987
|
+
}, [convNodes]);
|
|
2345
2988
|
const openSeq = openElem !== null && openElem.startsWith("n") ? Number(openElem.slice(1)) : null;
|
|
2346
|
-
const missingSeq = openSeq !== null && !bySeq.has(openSeq) ? openSeq : null;
|
|
2347
2989
|
const fetchContent = props.fetchContent;
|
|
2348
|
-
const
|
|
2349
|
-
const
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
if (!live) return;
|
|
2356
|
-
if (node === null) {
|
|
2357
|
-
setMissState("absent");
|
|
2358
|
-
return;
|
|
2359
|
-
}
|
|
2360
|
-
setFetched((prev) => {
|
|
2361
|
-
const next = new Map(prev);
|
|
2362
|
-
next.set(missingSeq, node);
|
|
2363
|
-
return next;
|
|
2364
|
-
});
|
|
2365
|
-
setMissState("idle");
|
|
2366
|
-
}, (error) => {
|
|
2367
|
-
console.warn("dsh-context: targeted history read failed", error);
|
|
2368
|
-
if (live) setMissState("failed");
|
|
2369
|
-
});
|
|
2370
|
-
return () => {
|
|
2371
|
-
live = false;
|
|
2372
|
-
};
|
|
2373
|
-
}, [
|
|
2374
|
-
missingSeq,
|
|
2375
|
-
fetchContent,
|
|
2376
|
-
retry
|
|
2377
|
-
]);
|
|
2990
|
+
const miss = useFetchOnMiss(openSeq !== null && !convBySeq.has(openSeq) ? openSeq : null, fetchContent, "dsh-context: targeted history read failed");
|
|
2991
|
+
const bySeq = React.useMemo(() => {
|
|
2992
|
+
if (miss.values.size === 0) return convBySeq;
|
|
2993
|
+
const m = new Map(convBySeq);
|
|
2994
|
+
for (const [seq, n] of miss.values) if (!m.has(seq)) m.set(seq, n);
|
|
2995
|
+
return m;
|
|
2996
|
+
}, [convBySeq, miss.values]);
|
|
2378
2997
|
const pinSeq = props.pinSeq;
|
|
2379
2998
|
React.useEffect(() => {
|
|
2380
2999
|
setSel(pinSeq === null || pinSeq === void 0 ? "live" : pinSeq);
|
|
@@ -2397,13 +3016,7 @@ window.__ModuleLoader__.load({
|
|
|
2397
3016
|
focusScrollRef.current = false;
|
|
2398
3017
|
rootRef.current?.querySelector(".lc-br-elem-on")?.scrollIntoView({ block: "nearest" });
|
|
2399
3018
|
});
|
|
2400
|
-
const missNote = fetchContent
|
|
2401
|
-
type: "button",
|
|
2402
|
-
className: "lc-br-retry",
|
|
2403
|
-
onClick: () => {
|
|
2404
|
-
setRetry((r) => r + 1);
|
|
2405
|
-
}
|
|
2406
|
-
}, t("browser.loadFailed")) : t("browser.noContent");
|
|
3019
|
+
const missNote = fetchMissNote(t, fetchContent, miss.state, miss.retry, "browser.noContent");
|
|
2407
3020
|
const requests = data.requests;
|
|
2408
3021
|
const hoverReq = props.previewSeq !== null && props.previewSeq !== void 0 ? requests.find((r) => r.seq === props.previewSeq) ?? null : null;
|
|
2409
3022
|
const req = hoverReq ?? (sel === "live" ? null : requests.find((r) => r.seq === sel) ?? null);
|
|
@@ -2411,48 +3024,11 @@ window.__ModuleLoader__.load({
|
|
|
2411
3024
|
const linked = req === null && props.onHoverKey !== void 0;
|
|
2412
3025
|
const linkKey = linked && props.hoverKey !== null && props.hoverKey !== "free" ? props.hoverKey : null;
|
|
2413
3026
|
const view = assemble(data, headers, seq);
|
|
2414
|
-
const [headerContent, setHeaderContent] = React.useState(() => /* @__PURE__ */ new Map());
|
|
2415
|
-
const [headerState, setHeaderState] = React.useState("idle");
|
|
2416
|
-
const [headerRetry, setHeaderRetry] = React.useState(0);
|
|
2417
3027
|
const fetchHeader = props.fetchHeader;
|
|
2418
3028
|
const headerSeq = view.header !== null ? view.header.seq : null;
|
|
2419
|
-
const
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
let live = true;
|
|
2423
|
-
setHeaderState("loading");
|
|
2424
|
-
fetchHeader(headerSeq).then((content) => {
|
|
2425
|
-
if (!live) return;
|
|
2426
|
-
if (content === null) {
|
|
2427
|
-
setHeaderState("absent");
|
|
2428
|
-
return;
|
|
2429
|
-
}
|
|
2430
|
-
setHeaderContent((prev) => {
|
|
2431
|
-
const next = new Map(prev);
|
|
2432
|
-
next.set(headerSeq, content);
|
|
2433
|
-
return next;
|
|
2434
|
-
});
|
|
2435
|
-
setHeaderState("idle");
|
|
2436
|
-
}, (error) => {
|
|
2437
|
-
console.warn("dsh-context: header content fetch failed", error);
|
|
2438
|
-
if (live) setHeaderState("failed");
|
|
2439
|
-
});
|
|
2440
|
-
return () => {
|
|
2441
|
-
live = false;
|
|
2442
|
-
};
|
|
2443
|
-
}, [
|
|
2444
|
-
headerNeeded,
|
|
2445
|
-
headerSeq,
|
|
2446
|
-
fetchHeader,
|
|
2447
|
-
headerRetry
|
|
2448
|
-
]);
|
|
2449
|
-
const headerNote = fetchHeader === void 0 ? t("browser.headerMetaOnly") : headerState === "loading" || headerState === "idle" ? t("browser.loading") : headerState === "absent" ? t("browser.notInLog") : /* @__PURE__ */ React.createElement("button", {
|
|
2450
|
-
type: "button",
|
|
2451
|
-
className: "lc-br-retry",
|
|
2452
|
-
onClick: () => {
|
|
2453
|
-
setHeaderRetry((r) => r + 1);
|
|
2454
|
-
}
|
|
2455
|
-
}, t("browser.loadFailed"));
|
|
3029
|
+
const epoch = useFetchOnMiss(headerSeq !== null && (openCat === "system" || openCat === "tools") ? headerSeq : null, fetchHeader, "dsh-context: header content fetch failed");
|
|
3030
|
+
const headerContent = epoch.values;
|
|
3031
|
+
const headerNote = fetchMissNote(t, fetchHeader, epoch.state, epoch.retry, "browser.headerMetaOnly");
|
|
2456
3032
|
const breakdown = req !== null ? req : data.current;
|
|
2457
3033
|
const parts = partsOf(breakdown);
|
|
2458
3034
|
const total = breakdown.total;
|
|
@@ -2658,7 +3234,14 @@ window.__ModuleLoader__.load({
|
|
|
2658
3234
|
hoverKey: linked ? linkKey : void 0,
|
|
2659
3235
|
onHoverKey: linked ? props.onHoverKey : void 0,
|
|
2660
3236
|
tip: false
|
|
2661
|
-
})), view.missingLive > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.missingLive", { n: view.missingLive })) : null, view.approximate ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.approx")) : null, /* @__PURE__ */ React.createElement(
|
|
3237
|
+
})), view.missingLive > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.missingLive", { n: view.missingLive })) : null, view.approximate ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.approx")) : null, props.detailState === "loading" ? /* @__PURE__ */ React.createElement(DetailNote, {
|
|
3238
|
+
state: "loading",
|
|
3239
|
+
className: "lc-br-note"
|
|
3240
|
+
}) : null, props.detailState === "failed" && props.onDetailRetry !== void 0 ? /* @__PURE__ */ React.createElement(DetailNote, {
|
|
3241
|
+
state: "failed",
|
|
3242
|
+
onRetry: props.onDetailRetry,
|
|
3243
|
+
className: "lc-br-note"
|
|
3244
|
+
}) : null, /* @__PURE__ */ React.createElement("div", { className: "lc-br-cats" }, CATS.map((c) => {
|
|
2662
3245
|
const count = toolCount(c.key);
|
|
2663
3246
|
const v = breakdown[c.key] || 0;
|
|
2664
3247
|
const prevCount = prevView !== null && prevByCat !== null ? countOf(prevView, prevByCat, c.key) : null;
|
|
@@ -2865,6 +3448,33 @@ window.__ModuleLoader__.load({
|
|
|
2865
3448
|
};
|
|
2866
3449
|
}
|
|
2867
3450
|
//#endregion
|
|
3451
|
+
//#region src/client/components/escapeClose.ts
|
|
3452
|
+
/**
|
|
3453
|
+
* The shared Escape-to-close contract of the plugin's overlays (the /context
|
|
3454
|
+
* modal and the baseline-gate dialog): capture-phase keydown on window, so
|
|
3455
|
+
* the composer's own key handling never swallows Escape first; on close, focus
|
|
3456
|
+
* returns to the element that held it when the overlay opened (skipped when
|
|
3457
|
+
* that element left the document).
|
|
3458
|
+
*/
|
|
3459
|
+
/** Close on Escape while `active`; restores the pre-open focus on cleanup. */
|
|
3460
|
+
function useEscapeClose(active, onClose) {
|
|
3461
|
+
React.useEffect(() => {
|
|
3462
|
+
if (!active) return void 0;
|
|
3463
|
+
const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
3464
|
+
const onKey = (ev) => {
|
|
3465
|
+
if (ev.key !== "Escape") return;
|
|
3466
|
+
ev.preventDefault();
|
|
3467
|
+
ev.stopPropagation();
|
|
3468
|
+
onClose();
|
|
3469
|
+
};
|
|
3470
|
+
window.addEventListener("keydown", onKey, true);
|
|
3471
|
+
return () => {
|
|
3472
|
+
window.removeEventListener("keydown", onKey, true);
|
|
3473
|
+
if (previous !== null && document.contains(previous)) previous.focus();
|
|
3474
|
+
};
|
|
3475
|
+
}, [active, onClose]);
|
|
3476
|
+
}
|
|
3477
|
+
//#endregion
|
|
2868
3478
|
//#region src/client/components/contextModal.tsx
|
|
2869
3479
|
function makeContextModal(ctx, kit) {
|
|
2870
3480
|
const { t } = kit;
|
|
@@ -2875,10 +3485,11 @@ window.__ModuleLoader__.load({
|
|
|
2875
3485
|
function ContextModalBody(props) {
|
|
2876
3486
|
const sessionId = typeof props.sessionId === "string" ? props.sessionId : "";
|
|
2877
3487
|
const open = typeof props.useContextModal === "function" ? props.useContextModal((s) => s) : false;
|
|
2878
|
-
const
|
|
2879
|
-
const
|
|
2880
|
-
const
|
|
2881
|
-
const
|
|
3488
|
+
const source = useTimelineSource(ctx, props);
|
|
3489
|
+
const data = source.data;
|
|
3490
|
+
const pressure = projectionOf(props, "contextPressure", contextPressureOf);
|
|
3491
|
+
const breakdown = projectionOf(props, "contextBreakdown", contextBreakdownOf);
|
|
3492
|
+
const headers = projectionOf(props, "contextHeaders", headersOf);
|
|
2882
3493
|
const convNodes = conversationNodesOf(props);
|
|
2883
3494
|
const [hoverCat, setHoverCat] = React.useState(null);
|
|
2884
3495
|
const [dockLeft, setDockLeft] = React.useState(0);
|
|
@@ -2894,21 +3505,7 @@ window.__ModuleLoader__.load({
|
|
|
2894
3505
|
const scope = sessions.scope(sessionId);
|
|
2895
3506
|
if (scope !== void 0) scope.bail(scope, "slash/input-consume-token", { guard });
|
|
2896
3507
|
}, [ctx, sessionId]);
|
|
2897
|
-
|
|
2898
|
-
if (!open) return void 0;
|
|
2899
|
-
const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
2900
|
-
const onKey = (ev) => {
|
|
2901
|
-
if (ev.key !== "Escape") return;
|
|
2902
|
-
ev.preventDefault();
|
|
2903
|
-
ev.stopPropagation();
|
|
2904
|
-
close();
|
|
2905
|
-
};
|
|
2906
|
-
window.addEventListener("keydown", onKey, true);
|
|
2907
|
-
return () => {
|
|
2908
|
-
window.removeEventListener("keydown", onKey, true);
|
|
2909
|
-
if (previous !== null && document.contains(previous)) previous.focus();
|
|
2910
|
-
};
|
|
2911
|
-
}, [open, close]);
|
|
3508
|
+
useEscapeClose(open, close);
|
|
2912
3509
|
React.useLayoutEffect(() => {
|
|
2913
3510
|
if (!open) return void 0;
|
|
2914
3511
|
const dock = measureDock(backdropRef.current);
|
|
@@ -2954,7 +3551,9 @@ window.__ModuleLoader__.load({
|
|
|
2954
3551
|
fetchContent,
|
|
2955
3552
|
fetchHeader,
|
|
2956
3553
|
hoverKey: hoverCat,
|
|
2957
|
-
onHoverKey: setHoverCat
|
|
3554
|
+
onHoverKey: setHoverCat,
|
|
3555
|
+
detailState: source.detailState,
|
|
3556
|
+
onDetailRetry: source.retryDetail
|
|
2958
3557
|
}))));
|
|
2959
3558
|
}
|
|
2960
3559
|
return function ContextModal(props) {
|
|
@@ -3154,6 +3753,17 @@ window.__ModuleLoader__.load({
|
|
|
3154
3753
|
state = next;
|
|
3155
3754
|
for (const listener of listeners) listener();
|
|
3156
3755
|
};
|
|
3756
|
+
const sync = (bound) => {
|
|
3757
|
+
const snap = bound.getSnapshot();
|
|
3758
|
+
const prefs = prefsOf(snap.value);
|
|
3759
|
+
publish({
|
|
3760
|
+
status: snap.status === "ready" || snap.status === "unavailable" ? snap.status : "loading",
|
|
3761
|
+
granularity: prefs.granularity ?? state.granularity,
|
|
3762
|
+
mode: prefs.mode ?? state.mode,
|
|
3763
|
+
fileSort: prefs.fileSort ?? state.fileSort,
|
|
3764
|
+
writable: snap.writable
|
|
3765
|
+
});
|
|
3766
|
+
};
|
|
3157
3767
|
return {
|
|
3158
3768
|
store: {
|
|
3159
3769
|
subscribe(listener) {
|
|
@@ -3169,205 +3779,86 @@ window.__ModuleLoader__.load({
|
|
|
3169
3779
|
defaultFileSort: () => state.fileSort,
|
|
3170
3780
|
attach(bound) {
|
|
3171
3781
|
scope = bound;
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
status: snap.status === "ready" || snap.status === "unavailable" ? snap.status : "loading",
|
|
3177
|
-
granularity: prefs.granularity ?? state.granularity,
|
|
3178
|
-
mode: prefs.mode ?? state.mode,
|
|
3179
|
-
fileSort: prefs.fileSort ?? state.fileSort,
|
|
3180
|
-
writable: snap.writable
|
|
3181
|
-
});
|
|
3182
|
-
};
|
|
3183
|
-
sync();
|
|
3184
|
-
return bound.subscribe(sync);
|
|
3782
|
+
sync(bound);
|
|
3783
|
+
return bound.subscribe(() => {
|
|
3784
|
+
sync(bound);
|
|
3785
|
+
});
|
|
3185
3786
|
},
|
|
3186
3787
|
set(field, value) {
|
|
3187
3788
|
publish({
|
|
3188
3789
|
...state,
|
|
3189
3790
|
...prefsOf({ [field]: value })
|
|
3190
3791
|
});
|
|
3191
|
-
scope
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
//#region src/client/brief.ts
|
|
3197
|
-
/** Every served node (live tail + removed archive copies), seq-sorted. */
|
|
3198
|
-
function briefNodes(data) {
|
|
3199
|
-
return [...data.nodes, ...data.archive].sort((a, b) => a.seq - b.seq);
|
|
3200
|
-
}
|
|
3201
|
-
/** First index whose node seq is >= `seq` (lower bound over the sorted list). */
|
|
3202
|
-
function lowerBound(nodes, seq) {
|
|
3203
|
-
let lo = 0;
|
|
3204
|
-
let hi = nodes.length;
|
|
3205
|
-
while (lo < hi) {
|
|
3206
|
-
const mid = lo + hi >> 1;
|
|
3207
|
-
if (nodes[mid].seq < seq) lo = mid + 1;
|
|
3208
|
-
else hi = mid;
|
|
3209
|
-
}
|
|
3210
|
-
return lo;
|
|
3211
|
-
}
|
|
3212
|
-
/**
|
|
3213
|
-
* Derive the brief for `requests[idx]` (the DISPLAY list — step records or
|
|
3214
|
-
* turn aggregates; a turn aggregate is always a turn start, so its inputs row
|
|
3215
|
-
* stays hidden and the opener/response carry the narrative).
|
|
3216
|
-
*/
|
|
3217
|
-
function briefOf(nodes, requests, idx) {
|
|
3218
|
-
if (idx < 0 || idx >= requests.length) return null;
|
|
3219
|
-
const req = requests[idx];
|
|
3220
|
-
const ri = lowerBound(nodes, req.seq);
|
|
3221
|
-
const hit = ri < nodes.length && nodes[ri].seq === req.seq ? nodes[ri] : void 0;
|
|
3222
|
-
const response = hit !== void 0 && hit.cat === "assistant" ? hit : void 0;
|
|
3223
|
-
const turnStart = idx === 0 || (requests[idx - 1].turn ?? 0) !== (req.turn ?? 0);
|
|
3224
|
-
let firstIdx = idx;
|
|
3225
|
-
while (firstIdx > 0 && (requests[firstIdx - 1].turn ?? 0) === (req.turn ?? 0)) firstIdx--;
|
|
3226
|
-
const upper = requests[firstIdx].seq;
|
|
3227
|
-
const lower = firstIdx > 0 ? requests[firstIdx - 1].seq : -1;
|
|
3228
|
-
let opener;
|
|
3229
|
-
for (let i = lowerBound(nodes, upper) - 1; i >= 0; i--) {
|
|
3230
|
-
const n = nodes[i];
|
|
3231
|
-
if (n.seq <= lower) break;
|
|
3232
|
-
if (n.cat === "user") {
|
|
3233
|
-
opener = n;
|
|
3234
|
-
break;
|
|
3792
|
+
const bound = scope;
|
|
3793
|
+
if (bound === void 0) return;
|
|
3794
|
+
bound.set(field, value).catch(() => {
|
|
3795
|
+
sync(bound);
|
|
3796
|
+
});
|
|
3235
3797
|
}
|
|
3236
|
-
}
|
|
3237
|
-
const inputs = [];
|
|
3238
|
-
if (!turnStart) {
|
|
3239
|
-
const prevSeq = requests[idx - 1].seq;
|
|
3240
|
-
for (let i = lowerBound(nodes, prevSeq + 1); i < nodes.length && nodes[i].seq < req.seq; i++) inputs.push(nodes[i]);
|
|
3241
|
-
}
|
|
3242
|
-
return {
|
|
3243
|
-
opener,
|
|
3244
|
-
inputs,
|
|
3245
|
-
response
|
|
3246
3798
|
};
|
|
3247
3799
|
}
|
|
3248
3800
|
//#endregion
|
|
3249
|
-
//#region src/client/
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
write: "write",
|
|
3254
|
-
edit: "write",
|
|
3255
|
-
grep: "search",
|
|
3256
|
-
glob: "search"
|
|
3257
|
-
};
|
|
3258
|
-
/** The file purpose of a tool, or null for non-file tools (bash, web_search…). */
|
|
3259
|
-
function kindOfTool(tool) {
|
|
3260
|
-
if (tool === void 0) return null;
|
|
3261
|
-
return KIND_BY_TOOL[tool] ?? null;
|
|
3262
|
-
}
|
|
3263
|
-
/**
|
|
3264
|
-
* The file purpose of one executed call. Like {@link kindOfTool} except for
|
|
3265
|
-
* the one file tool whose purpose follows its arguments: `str_replace_editor`
|
|
3266
|
-
* reads on `view` and writes on every other command (create / str_replace /
|
|
3267
|
-
* insert — an unknown command writes too; the call failed and the row keeps
|
|
3268
|
-
* its error flag).
|
|
3269
|
-
*/
|
|
3270
|
-
function kindOfCall(tool, args) {
|
|
3271
|
-
if (tool === "str_replace_editor") return args !== null && args.command === "view" ? "read" : "write";
|
|
3272
|
-
return kindOfTool(tool);
|
|
3273
|
-
}
|
|
3274
|
-
/**
|
|
3275
|
-
* The operation's target path: the path-ish argument of read/write tools;
|
|
3276
|
-
* for searches the narrowing `path`, else the pattern itself (a pathless
|
|
3277
|
-
* grep/glob's target IS the pattern — the workspace-wide search text).
|
|
3278
|
-
*/
|
|
3279
|
-
function pathOfArgs(tool, args) {
|
|
3280
|
-
if (args === null) return null;
|
|
3281
|
-
if (tool === "grep" || tool === "glob") {
|
|
3282
|
-
const p = args.path;
|
|
3283
|
-
if (typeof p === "string" && p !== "") return p;
|
|
3284
|
-
const pattern = args.pattern;
|
|
3285
|
-
return typeof pattern === "string" && pattern !== "" ? pattern : null;
|
|
3286
|
-
}
|
|
3287
|
-
for (const k of [
|
|
3288
|
-
"file_path",
|
|
3289
|
-
"filePath",
|
|
3290
|
-
"path"
|
|
3291
|
-
]) {
|
|
3292
|
-
const v = args[k];
|
|
3293
|
-
if (typeof v === "string" && v !== "") return v;
|
|
3294
|
-
}
|
|
3295
|
-
return null;
|
|
3296
|
-
}
|
|
3297
|
-
/** Rendered line count: '' is 0, a trailing newline closes its own line. */
|
|
3298
|
-
function linesOf(s) {
|
|
3299
|
-
if (s === "") return 0;
|
|
3300
|
-
let n = 0;
|
|
3301
|
-
for (let i = 0; i < s.length; i++) if (s[i] === "\n") n++;
|
|
3302
|
-
return s.endsWith("\n") ? n : n + 1;
|
|
3303
|
-
}
|
|
3304
|
-
/** The added/removed pair of one content-bearing argument set, or zeros. */
|
|
3305
|
-
function pairOf(added, removed) {
|
|
3306
|
-
return {
|
|
3307
|
-
added: typeof added === "string" ? linesOf(added) : 0,
|
|
3308
|
-
removed: typeof removed === "string" ? linesOf(removed) : 0
|
|
3309
|
-
};
|
|
3310
|
-
}
|
|
3311
|
-
/**
|
|
3312
|
-
* The signed line footprint of one call: an edit removes its old string and
|
|
3313
|
-
* adds its new one; a write adds its content (the pre-existing body, if any,
|
|
3314
|
-
* is unknowable from the arguments — the estimate stays honest about that);
|
|
3315
|
-
* `str_replace_editor` splits the same shapes across its commands. Callers
|
|
3316
|
-
* reach here only with parsed args (a null parse yields no path).
|
|
3317
|
-
*/
|
|
3318
|
-
function deltaOf(tool, args) {
|
|
3319
|
-
if (tool === "edit") return pairOf(args.new_string, args.old_string);
|
|
3320
|
-
if (tool === "write") return pairOf(args.content, void 0);
|
|
3321
|
-
if (tool === "str_replace_editor") {
|
|
3322
|
-
if (args.command === "str_replace") return pairOf(args.new_str, args.old_str);
|
|
3323
|
-
if (args.command === "insert") return pairOf(args.new_str, void 0);
|
|
3324
|
-
if (args.command === "create") return pairOf(args.file_text, void 0);
|
|
3325
|
-
}
|
|
3326
|
-
return {
|
|
3327
|
-
added: 0,
|
|
3328
|
-
removed: 0
|
|
3329
|
-
};
|
|
3330
|
-
}
|
|
3331
|
-
/** The exact window a read's result meta reports: `offset` plus the retained
|
|
3332
|
-
* `lines` array (the same bounded payload the read card renders from). Null
|
|
3333
|
-
* for a foreign or malformed meta. */
|
|
3334
|
-
function readWindowOf(meta) {
|
|
3335
|
-
if (meta === null || typeof meta !== "object") return null;
|
|
3336
|
-
const m = meta;
|
|
3337
|
-
if (typeof m.path !== "string" || m.path === "") return null;
|
|
3338
|
-
if (typeof m.offset !== "number" || !Number.isFinite(m.offset) || m.offset < 1) return null;
|
|
3339
|
-
if (!Array.isArray(m.lines) || m.lines.length === 0) return null;
|
|
3340
|
-
return {
|
|
3341
|
-
start: m.offset,
|
|
3342
|
-
count: m.lines.length
|
|
3343
|
-
};
|
|
3801
|
+
//#region src/client/brief.ts
|
|
3802
|
+
/** Every served node (live tail + removed archive copies), seq-sorted. */
|
|
3803
|
+
function briefNodes(data) {
|
|
3804
|
+
return [...data.nodes, ...data.archive].sort((a, b) => a.seq - b.seq);
|
|
3344
3805
|
}
|
|
3345
|
-
/**
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3806
|
+
/** First index whose node seq is >= `seq` (lower bound over the sorted list). */
|
|
3807
|
+
function lowerBound(nodes, seq) {
|
|
3808
|
+
let lo = 0;
|
|
3809
|
+
let hi = nodes.length;
|
|
3810
|
+
while (lo < hi) {
|
|
3811
|
+
const mid = lo + hi >> 1;
|
|
3812
|
+
if (nodes[mid].seq < seq) lo = mid + 1;
|
|
3813
|
+
else hi = mid;
|
|
3814
|
+
}
|
|
3815
|
+
return lo;
|
|
3353
3816
|
}
|
|
3354
|
-
/**
|
|
3355
|
-
*
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3817
|
+
/**
|
|
3818
|
+
* Derive the brief for `requests[idx]` (the DISPLAY list — step records or
|
|
3819
|
+
* turn aggregates; a turn aggregate is always a turn start, so its inputs row
|
|
3820
|
+
* stays hidden and the opener/response carry the narrative).
|
|
3821
|
+
*/
|
|
3822
|
+
function briefOf(nodes, requests, idx) {
|
|
3823
|
+
if (idx < 0 || idx >= requests.length) return null;
|
|
3824
|
+
const req = requests[idx];
|
|
3825
|
+
const ri = lowerBound(nodes, req.seq);
|
|
3826
|
+
const hit = ri < nodes.length && nodes[ri].seq === req.seq ? nodes[ri] : void 0;
|
|
3827
|
+
const response = hit !== void 0 && hit.cat === "assistant" ? hit : void 0;
|
|
3828
|
+
const turnStart = idx === 0 || (requests[idx - 1].turn ?? 0) !== (req.turn ?? 0);
|
|
3829
|
+
let firstIdx = idx;
|
|
3830
|
+
while (firstIdx > 0 && (requests[firstIdx - 1].turn ?? 0) === (req.turn ?? 0)) firstIdx--;
|
|
3831
|
+
const upper = requests[firstIdx].seq;
|
|
3832
|
+
const lower = firstIdx > 0 ? requests[firstIdx - 1].seq : -1;
|
|
3833
|
+
let opener;
|
|
3834
|
+
for (let i = lowerBound(nodes, upper) - 1; i >= 0; i--) {
|
|
3835
|
+
const n = nodes[i];
|
|
3836
|
+
if (n.seq <= lower) break;
|
|
3837
|
+
if (n.cat === "user") {
|
|
3838
|
+
opener = n;
|
|
3839
|
+
break;
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
const inputs = [];
|
|
3843
|
+
if (!turnStart) {
|
|
3844
|
+
const prevSeq = requests[idx - 1].seq;
|
|
3845
|
+
for (let i = lowerBound(nodes, prevSeq + 1); i < nodes.length && nodes[i].seq < req.seq; i++) inputs.push(nodes[i]);
|
|
3846
|
+
}
|
|
3847
|
+
return {
|
|
3848
|
+
opener,
|
|
3849
|
+
inputs,
|
|
3850
|
+
response
|
|
3361
3851
|
};
|
|
3362
|
-
return readEstimateOf(args);
|
|
3363
3852
|
}
|
|
3364
|
-
|
|
3853
|
+
//#endregion
|
|
3854
|
+
//#region src/client/fileActivity.ts
|
|
3365
3855
|
/** The file's form — multimodal reads and image extensions scan apart; a trailing slash marks a directory target. */
|
|
3366
3856
|
function formOf(tool, path) {
|
|
3367
3857
|
if (tool === "read_image" || IMAGE_EXT.test(path)) return "image";
|
|
3368
3858
|
if (path.endsWith("/")) return "dir";
|
|
3369
3859
|
return "text";
|
|
3370
3860
|
}
|
|
3861
|
+
const IMAGE_EXT = /\.(png|jpe?g|gif|webp|svg|bmp|ico|avif)$/i;
|
|
3371
3862
|
/** Directory buckets, by last path segment (lowercased, explicit plurals). Checked in order, then hidden dirs, then the plain folder. */
|
|
3372
3863
|
const DIR_BUCKETS = [
|
|
3373
3864
|
[
|
|
@@ -3866,118 +4357,11 @@ window.__ModuleLoader__.load({
|
|
|
3866
4357
|
return "./" + path;
|
|
3867
4358
|
}
|
|
3868
4359
|
/**
|
|
3869
|
-
*
|
|
3870
|
-
*
|
|
3871
|
-
|
|
3872
|
-
function searchDetailOf(args) {
|
|
3873
|
-
const pattern = args?.pattern;
|
|
3874
|
-
if (typeof pattern !== "string" || pattern === "") return void 0;
|
|
3875
|
-
const include = args?.include;
|
|
3876
|
-
return typeof include === "string" && include !== "" ? `${pattern} (${include})` : pattern;
|
|
3877
|
-
}
|
|
3878
|
-
/**
|
|
3879
|
-
* The files a search demonstrably reached, read off the result's bounded
|
|
3880
|
-
* presentation meta (grep groups matched lines by file; glob lists paths).
|
|
3881
|
-
* Only the COMPLETE list attributes: a capped search (`truncated`) names a
|
|
3882
|
-
* partial file set, and a malformed meta names none — both fall back to the
|
|
3883
|
-
* call's own target. Each entry carries the reported match count.
|
|
3884
|
-
*/
|
|
3885
|
-
function searchFilesOf(meta) {
|
|
3886
|
-
if (meta === null || typeof meta !== "object") return null;
|
|
3887
|
-
const m = meta;
|
|
3888
|
-
if (m.truncated !== false) return null;
|
|
3889
|
-
const files = [];
|
|
3890
|
-
if (m.shape === "matches" && Array.isArray(m.files)) for (const f of m.files) {
|
|
3891
|
-
if (f === null || typeof f !== "object") continue;
|
|
3892
|
-
const group = f;
|
|
3893
|
-
if (typeof group.path === "string" && group.path !== "" && Array.isArray(group.matches)) files.push({
|
|
3894
|
-
path: group.path,
|
|
3895
|
-
hits: group.matches.length
|
|
3896
|
-
});
|
|
3897
|
-
}
|
|
3898
|
-
else if (m.shape === "paths" && Array.isArray(m.paths)) {
|
|
3899
|
-
for (const p of m.paths) if (typeof p === "string" && p !== "") files.push({
|
|
3900
|
-
path: p,
|
|
3901
|
-
hits: 0
|
|
3902
|
-
});
|
|
3903
|
-
}
|
|
3904
|
-
return files.length > 0 ? files : null;
|
|
3905
|
-
}
|
|
3906
|
-
/**
|
|
3907
|
-
* Narrow one block of a conversation node's `subCalls` tree to a settled
|
|
3908
|
-
* nested call, or null. The join is defensive — a running call has no result
|
|
3909
|
-
* kind yet, and any malformed block is dropped, never thrown. (Null and
|
|
3910
|
-
* non-object blocks never reach here: the folding loop pre-filters them.)
|
|
3911
|
-
*/
|
|
3912
|
-
function subCallOf(block) {
|
|
3913
|
-
const b = block;
|
|
3914
|
-
if (b.kind !== "tool-result") return null;
|
|
3915
|
-
if (b.call === null || typeof b.call !== "object") return null;
|
|
3916
|
-
const call = b.call;
|
|
3917
|
-
if (typeof call.name !== "string" || typeof call.argsRaw !== "string") return null;
|
|
3918
|
-
if (typeof b.seq !== "number" || !Number.isFinite(b.seq)) return null;
|
|
3919
|
-
return {
|
|
3920
|
-
name: call.name,
|
|
3921
|
-
argsRaw: call.argsRaw,
|
|
3922
|
-
seq: b.seq,
|
|
3923
|
-
...typeof b.time === "number" ? { time: b.time } : {},
|
|
3924
|
-
...b.isError === true ? { err: true } : { err: false },
|
|
3925
|
-
...Array.isArray(b.subCalls) ? { subCalls: b.subCalls } : {}
|
|
3926
|
-
};
|
|
3927
|
-
}
|
|
3928
|
-
/** The run_code call's model-authored description — the nested ops' "why". */
|
|
3929
|
-
function programOf(conv) {
|
|
3930
|
-
const description = parseCallArgs(conv?.call?.argsRaw)?.description;
|
|
3931
|
-
return typeof description === "string" && description !== "" ? description : void 0;
|
|
3932
|
-
}
|
|
3933
|
-
/**
|
|
3934
|
-
* Depth guard for nested Code-Mode trees. The SDK bindings exclude `run_code`
|
|
3935
|
-
* itself, so a real tree is one level deep; the cap only bounds defensive
|
|
3936
|
-
* re-entry over a malformed join.
|
|
3937
|
-
*/
|
|
3938
|
-
const SUBCALL_MAX_DEPTH = 8;
|
|
3939
|
-
/**
|
|
3940
|
-
* Fold one nested Code-Mode tree: every settled sub-dispatch whose arguments
|
|
3941
|
-
* resolve to a file target books one op, attributed to the nested tool and
|
|
3942
|
-
* located on the parent run_code result. `seen` holds the already-visited
|
|
3943
|
-
* blocks, so a malformed (cyclic) join cannot loop.
|
|
4360
|
+
* Fold op records into per-file activity: scope-filtered, aggregated per
|
|
4361
|
+
* path, ops newest first. The presentation half of the card — the parsing
|
|
4362
|
+
* is the shared parser's (shared/fileOps.ts), however the records arrived.
|
|
3944
4363
|
*/
|
|
3945
|
-
function
|
|
3946
|
-
if (depth > SUBCALL_MAX_DEPTH) return;
|
|
3947
|
-
for (const block of blocks) {
|
|
3948
|
-
if (block === null || typeof block !== "object" || seen.has(block)) continue;
|
|
3949
|
-
seen.add(block);
|
|
3950
|
-
const sub = subCallOf(block);
|
|
3951
|
-
if (sub !== null) {
|
|
3952
|
-
const args = parseCallArgs(sub.argsRaw);
|
|
3953
|
-
const kind = args !== null ? kindOfCall(sub.name, args) : null;
|
|
3954
|
-
const path = kind !== null ? pathOfArgs(sub.name, args) : null;
|
|
3955
|
-
if (args !== null && kind !== null && path !== null) {
|
|
3956
|
-
const { added, removed } = deltaOf(sub.name, args);
|
|
3957
|
-
const narrowed = typeof args.path === "string" && args.path !== "";
|
|
3958
|
-
const detail = kind === "search" && narrowed ? searchDetailOf(args) : void 0;
|
|
3959
|
-
const read = kind === "read" ? readOf(void 0, args) : void 0;
|
|
3960
|
-
add({
|
|
3961
|
-
seq: sub.seq,
|
|
3962
|
-
...parent.gone !== void 0 ? { gone: parent.gone } : {},
|
|
3963
|
-
kind,
|
|
3964
|
-
tool: sub.name,
|
|
3965
|
-
...sub.time !== void 0 ? { time: sub.time } : {},
|
|
3966
|
-
err: sub.err,
|
|
3967
|
-
added,
|
|
3968
|
-
removed,
|
|
3969
|
-
...detail !== void 0 ? { detail } : {},
|
|
3970
|
-
...read !== void 0 ? { read } : {},
|
|
3971
|
-
parent: parent.seq,
|
|
3972
|
-
...program !== void 0 ? { program } : {}
|
|
3973
|
-
}, path, kind === "search" && !narrowed);
|
|
3974
|
-
}
|
|
3975
|
-
if (sub.subCalls !== void 0) foldSubCalls(sub.subCalls, parent, program, add, seen, depth + 1);
|
|
3976
|
-
}
|
|
3977
|
-
}
|
|
3978
|
-
}
|
|
3979
|
-
/** Fold every served tool-result node into per-file activity. */
|
|
3980
|
-
function activityOf(nodes, convOf, before) {
|
|
4364
|
+
function aggregateOps(ops, before) {
|
|
3981
4365
|
const totals = {
|
|
3982
4366
|
read: {
|
|
3983
4367
|
files: 0,
|
|
@@ -3999,15 +4383,15 @@ window.__ModuleLoader__.load({
|
|
|
3999
4383
|
removed: 0
|
|
4000
4384
|
};
|
|
4001
4385
|
const byPath = /* @__PURE__ */ new Map();
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4386
|
+
for (const op of ops) {
|
|
4387
|
+
const key = op.parent ?? op.seq;
|
|
4388
|
+
if (before !== null && key >= before) continue;
|
|
4005
4389
|
totals[op.kind].ops++;
|
|
4006
|
-
let entry = byPath.get(path);
|
|
4390
|
+
let entry = byPath.get(op.path);
|
|
4007
4391
|
if (entry === void 0) {
|
|
4008
4392
|
entry = {
|
|
4009
|
-
path,
|
|
4010
|
-
form: formOf(op.tool, path),
|
|
4393
|
+
path: op.path,
|
|
4394
|
+
form: formOf(op.tool, op.path),
|
|
4011
4395
|
reads: 0,
|
|
4012
4396
|
writes: 0,
|
|
4013
4397
|
searches: 0,
|
|
@@ -4015,9 +4399,9 @@ window.__ModuleLoader__.load({
|
|
|
4015
4399
|
removed: 0,
|
|
4016
4400
|
errs: 0,
|
|
4017
4401
|
ops: [],
|
|
4018
|
-
...pattern ? { pattern: true } : {}
|
|
4402
|
+
...op.pattern === true ? { pattern: true } : {}
|
|
4019
4403
|
};
|
|
4020
|
-
byPath.set(path, entry);
|
|
4404
|
+
byPath.set(op.path, entry);
|
|
4021
4405
|
}
|
|
4022
4406
|
if (op.kind === "read") entry.reads++;
|
|
4023
4407
|
else if (op.kind === "write") entry.writes++;
|
|
@@ -4026,59 +4410,6 @@ window.__ModuleLoader__.load({
|
|
|
4026
4410
|
entry.removed += op.removed;
|
|
4027
4411
|
if (op.err) entry.errs++;
|
|
4028
4412
|
entry.ops.push(op);
|
|
4029
|
-
};
|
|
4030
|
-
for (const n of nodes) {
|
|
4031
|
-
if (n.cat !== "tool") continue;
|
|
4032
|
-
if (before !== null && n.seq >= before) continue;
|
|
4033
|
-
const tool = n.tool;
|
|
4034
|
-
if (tool === void 0) continue;
|
|
4035
|
-
try {
|
|
4036
|
-
const conv = convOf(n.seq);
|
|
4037
|
-
const staticKind = kindOfTool(tool);
|
|
4038
|
-
const args = staticKind !== null || tool === "str_replace_editor" ? parseCallArgs(conv?.call?.argsRaw) : null;
|
|
4039
|
-
const kind = staticKind ?? kindOfCall(tool, args);
|
|
4040
|
-
if (kind !== null) {
|
|
4041
|
-
const err = n.err === true || conv?.isError === true;
|
|
4042
|
-
const files = kind === "search" ? searchFilesOf(conv?.meta) : null;
|
|
4043
|
-
if (files !== null) {
|
|
4044
|
-
const detail = searchDetailOf(args);
|
|
4045
|
-
for (const f of files) add({
|
|
4046
|
-
seq: n.seq,
|
|
4047
|
-
...n.gone !== void 0 ? { gone: n.gone } : {},
|
|
4048
|
-
kind,
|
|
4049
|
-
tool,
|
|
4050
|
-
...n.time !== void 0 ? { time: n.time } : {},
|
|
4051
|
-
err,
|
|
4052
|
-
added: 0,
|
|
4053
|
-
removed: 0,
|
|
4054
|
-
...detail !== void 0 ? { detail } : {},
|
|
4055
|
-
...f.hits > 0 ? { hits: f.hits } : {}
|
|
4056
|
-
}, f.path);
|
|
4057
|
-
} else if (args !== null) {
|
|
4058
|
-
const path = pathOfArgs(tool, args);
|
|
4059
|
-
if (path !== null) {
|
|
4060
|
-
const { added, removed } = deltaOf(tool, args);
|
|
4061
|
-
const narrowed = typeof args.path === "string" && args.path !== "";
|
|
4062
|
-
const detail = kind === "search" && narrowed ? searchDetailOf(args) : void 0;
|
|
4063
|
-
const read = kind === "read" ? readOf(conv?.meta, args) : void 0;
|
|
4064
|
-
add({
|
|
4065
|
-
seq: n.seq,
|
|
4066
|
-
...n.gone !== void 0 ? { gone: n.gone } : {},
|
|
4067
|
-
kind,
|
|
4068
|
-
tool,
|
|
4069
|
-
...n.time !== void 0 ? { time: n.time } : {},
|
|
4070
|
-
err,
|
|
4071
|
-
added,
|
|
4072
|
-
removed,
|
|
4073
|
-
...detail !== void 0 ? { detail } : {},
|
|
4074
|
-
...read !== void 0 ? { read } : {}
|
|
4075
|
-
}, path, kind === "search" && !narrowed);
|
|
4076
|
-
}
|
|
4077
|
-
}
|
|
4078
|
-
}
|
|
4079
|
-
const subCalls = conv?.subCalls;
|
|
4080
|
-
if (subCalls !== void 0 && subCalls.length > 0) foldSubCalls(subCalls, n, programOf(conv), add, /* @__PURE__ */ new Set(), 1);
|
|
4081
|
-
} catch {}
|
|
4082
4413
|
}
|
|
4083
4414
|
const entries = [...byPath.values()];
|
|
4084
4415
|
for (const e of entries) {
|
|
@@ -4100,6 +4431,114 @@ window.__ModuleLoader__.load({
|
|
|
4100
4431
|
};
|
|
4101
4432
|
}
|
|
4102
4433
|
/**
|
|
4434
|
+
* The op-log generation's read of the card: the fold-derived records (the
|
|
4435
|
+
* detail payload's `fileOps`), with `gone` joined from the detail's archive
|
|
4436
|
+
* at render time (the op's result node leaving the live surface bounds where
|
|
4437
|
+
* its content stays viewable — the locate bridge reads it).
|
|
4438
|
+
*/
|
|
4439
|
+
function activityOfOps(ops, archive, before) {
|
|
4440
|
+
const goneBySeq = /* @__PURE__ */ new Map();
|
|
4441
|
+
for (const n of archive) if (n.gone !== void 0) goneBySeq.set(n.seq, n.gone);
|
|
4442
|
+
return aggregateOps(ops.map((op) => {
|
|
4443
|
+
const gone = goneBySeq.get(op.parent ?? op.seq);
|
|
4444
|
+
return gone !== void 0 && op.gone === void 0 ? {
|
|
4445
|
+
...op,
|
|
4446
|
+
gone
|
|
4447
|
+
} : op;
|
|
4448
|
+
}), before);
|
|
4449
|
+
}
|
|
4450
|
+
/**
|
|
4451
|
+
* Narrow one block of a conversation node's `subCalls` tree to a settled
|
|
4452
|
+
* nested call, or null. The join is defensive — a running call has no result
|
|
4453
|
+
* kind yet, and any malformed block is dropped, never thrown. (Null and
|
|
4454
|
+
* non-object blocks never reach here: the folding loop pre-filters them.)
|
|
4455
|
+
*/
|
|
4456
|
+
function subCallOf(block) {
|
|
4457
|
+
const b = block;
|
|
4458
|
+
if (b.kind !== "tool-result") return null;
|
|
4459
|
+
if (b.call === null || typeof b.call !== "object") return null;
|
|
4460
|
+
const call = b.call;
|
|
4461
|
+
if (typeof call.name !== "string" || typeof call.argsRaw !== "string") return null;
|
|
4462
|
+
if (typeof b.seq !== "number" || !Number.isFinite(b.seq)) return null;
|
|
4463
|
+
return {
|
|
4464
|
+
name: call.name,
|
|
4465
|
+
argsRaw: call.argsRaw,
|
|
4466
|
+
seq: b.seq,
|
|
4467
|
+
...typeof b.time === "number" ? { time: b.time } : {},
|
|
4468
|
+
...b.isError === true ? { err: true } : { err: false },
|
|
4469
|
+
...Array.isArray(b.subCalls) ? { subCalls: b.subCalls } : {}
|
|
4470
|
+
};
|
|
4471
|
+
}
|
|
4472
|
+
/** The run_code call's model-authored description — the nested ops' "why". */
|
|
4473
|
+
function programOf(conv) {
|
|
4474
|
+
const description = parseCallArgs(conv?.call?.argsRaw)?.description;
|
|
4475
|
+
return typeof description === "string" && description !== "" ? description : void 0;
|
|
4476
|
+
}
|
|
4477
|
+
/**
|
|
4478
|
+
* Depth guard for nested Code-Mode trees. The SDK bindings exclude `run_code`
|
|
4479
|
+
* itself, so a real tree is one level deep; the cap only bounds defensive
|
|
4480
|
+
* re-entry over a malformed join.
|
|
4481
|
+
*/
|
|
4482
|
+
const SUBCALL_MAX_DEPTH = 8;
|
|
4483
|
+
/**
|
|
4484
|
+
* The legacy nested walk: every settled sub-dispatch whose arguments resolve
|
|
4485
|
+
* to a file target books one op, attributed to the nested tool and located
|
|
4486
|
+
* on the parent run_code result. `seen` holds the already-visited blocks, so
|
|
4487
|
+
* a malformed (cyclic) join cannot loop.
|
|
4488
|
+
*/
|
|
4489
|
+
function foldSubCalls(blocks, parent, program, ops, seen, depth) {
|
|
4490
|
+
if (depth > SUBCALL_MAX_DEPTH) return;
|
|
4491
|
+
for (const block of blocks) {
|
|
4492
|
+
if (block === null || typeof block !== "object" || seen.has(block)) continue;
|
|
4493
|
+
seen.add(block);
|
|
4494
|
+
const sub = subCallOf(block);
|
|
4495
|
+
if (sub !== null) {
|
|
4496
|
+
ops.push(...opsOfCall({
|
|
4497
|
+
seq: sub.seq,
|
|
4498
|
+
tool: sub.name,
|
|
4499
|
+
argsRaw: sub.argsRaw,
|
|
4500
|
+
err: sub.err,
|
|
4501
|
+
...sub.time !== void 0 ? { time: sub.time } : {},
|
|
4502
|
+
...parent.gone !== void 0 ? { gone: parent.gone } : {},
|
|
4503
|
+
parent: parent.seq,
|
|
4504
|
+
...program !== void 0 ? { program } : {}
|
|
4505
|
+
}));
|
|
4506
|
+
if (sub.subCalls !== void 0) foldSubCalls(sub.subCalls, parent, program, ops, seen, depth + 1);
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
4510
|
+
/**
|
|
4511
|
+
* The INLINE generation's derivation: ops from the served tool-result nodes
|
|
4512
|
+
* joined with the conversation window (arguments/meta live on the join —
|
|
4513
|
+
* window-bound; the op-log generation covers the full session instead).
|
|
4514
|
+
* One node's join data can never take the card down: anything that throws
|
|
4515
|
+
* while folding it drops that node and the walk carries on.
|
|
4516
|
+
*/
|
|
4517
|
+
function activityOf(nodes, convOf, before) {
|
|
4518
|
+
const ops = [];
|
|
4519
|
+
for (const n of nodes) {
|
|
4520
|
+
if (n.cat !== "tool") continue;
|
|
4521
|
+
if (before !== null && n.seq >= before) continue;
|
|
4522
|
+
const tool = n.tool;
|
|
4523
|
+
if (tool === void 0) continue;
|
|
4524
|
+
try {
|
|
4525
|
+
const conv = convOf(n.seq);
|
|
4526
|
+
ops.push(...opsOfCall({
|
|
4527
|
+
seq: n.seq,
|
|
4528
|
+
tool,
|
|
4529
|
+
argsRaw: conv?.call?.argsRaw,
|
|
4530
|
+
meta: conv?.meta,
|
|
4531
|
+
err: n.err === true || conv?.isError === true,
|
|
4532
|
+
...n.time !== void 0 ? { time: n.time } : {},
|
|
4533
|
+
...n.gone !== void 0 ? { gone: n.gone } : {}
|
|
4534
|
+
}));
|
|
4535
|
+
const subCalls = conv?.subCalls;
|
|
4536
|
+
if (subCalls !== void 0 && subCalls.length > 0) foldSubCalls(subCalls, n, programOf(conv), ops, /* @__PURE__ */ new Set(), 1);
|
|
4537
|
+
} catch {}
|
|
4538
|
+
}
|
|
4539
|
+
return aggregateOps(ops, null);
|
|
4540
|
+
}
|
|
4541
|
+
/**
|
|
4103
4542
|
* The browser step whose assembled surface SHOWS an op's result node: the
|
|
4104
4543
|
* first request dispatched after it while it was still alive (that step's
|
|
4105
4544
|
* brief is where the result landed). A live node no request has consumed
|
|
@@ -4116,10 +4555,6 @@ window.__ModuleLoader__.load({
|
|
|
4116
4555
|
const LEVEL_H = 154;
|
|
4117
4556
|
const CELL_H = 90;
|
|
4118
4557
|
const PAD_Y = 56;
|
|
4119
|
-
function asRecord(value) {
|
|
4120
|
-
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
4121
|
-
return value;
|
|
4122
|
-
}
|
|
4123
4558
|
/** Narrow one list-row value; null when it is not a row at all. */
|
|
4124
4559
|
function agentRowOf(value) {
|
|
4125
4560
|
const rec = asRecord(value);
|
|
@@ -4185,7 +4620,7 @@ window.__ModuleLoader__.load({
|
|
|
4185
4620
|
const billed = usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null;
|
|
4186
4621
|
return {
|
|
4187
4622
|
head,
|
|
4188
|
-
requests: timeline !== null ? timeline.requests.length : 0,
|
|
4623
|
+
requests: timeline !== null ? timeline.counts?.steps ?? timeline.requests.length : 0,
|
|
4189
4624
|
billed,
|
|
4190
4625
|
durationMs: agentDurationOf(values?.subagentTiming),
|
|
4191
4626
|
identity: agentIdentityOf(values?.subagent)
|
|
@@ -4489,8 +4924,13 @@ window.__ModuleLoader__.load({
|
|
|
4489
4924
|
if (list === null || typeof list.getSnapshot !== "function" || typeof list.subscribe !== "function") return null;
|
|
4490
4925
|
return rec;
|
|
4491
4926
|
}
|
|
4492
|
-
/**
|
|
4493
|
-
|
|
4927
|
+
/**
|
|
4928
|
+
* Compact duration: `42s`, `3m05s`, `1h07m` (shared by both locales).
|
|
4929
|
+
* Deliberately distinct from format.ts's locale-aware `fmtDuration` (the
|
|
4930
|
+
* timing card's `12.3s` / `4分0秒`): the inspector's caption column needs
|
|
4931
|
+
* whole-second, fixed-width, locale-free text.
|
|
4932
|
+
*/
|
|
4933
|
+
function fmtDurationCompact(ms) {
|
|
4494
4934
|
if (!Number.isFinite(ms) || ms < 0) return "—";
|
|
4495
4935
|
const s = Math.round(ms / 1e3);
|
|
4496
4936
|
if (s < 60) return `${s}s`;
|
|
@@ -4689,7 +5129,7 @@ window.__ModuleLoader__.load({
|
|
|
4689
5129
|
}
|
|
4690
5130
|
if (node.requests > 0) bits.push(t("agents.requests", { n: node.requests }));
|
|
4691
5131
|
if (node.billed !== null && node.billed > 0) bits.push(t("agents.billed", { n: fmt(node.billed) }));
|
|
4692
|
-
if (node.durationMs !== null) bits.push(
|
|
5132
|
+
if (node.durationMs !== null) bits.push(fmtDurationCompact(node.durationMs));
|
|
4693
5133
|
return /* @__PURE__ */ React.createElement("div", { className: "lc-agents-inspector" }, /* @__PURE__ */ React.createElement("b", { className: "lc-agents-inspector-name" }, node.label), node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge" }, t("agents.self")) : null, node.running ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge lc-agents-badge-on" }, t("agents.running")) : null, node.identity !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge" }, t(node.identity.mode === "one-shot" ? "agents.oneshot" : "agents.continuable")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-agents-inspector-stats" }, bits.join(" · ")), !node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-inspector-open" }, t("agents.open")) : null);
|
|
4694
5134
|
}
|
|
4695
5135
|
//#endregion
|
|
@@ -4824,6 +5264,7 @@ window.__ModuleLoader__.load({
|
|
|
4824
5264
|
}
|
|
4825
5265
|
function makeEventList(kit) {
|
|
4826
5266
|
const { t, fmt, fmtTime, eventLabel, eventAt } = kit;
|
|
5267
|
+
const DetailNote = makeDetailNote(kit);
|
|
4827
5268
|
return function EventList(props) {
|
|
4828
5269
|
const rootRef = React.useRef(null);
|
|
4829
5270
|
React.useLayoutEffect(() => {
|
|
@@ -4841,7 +5282,14 @@ window.__ModuleLoader__.load({
|
|
|
4841
5282
|
window.removeEventListener("resize", onResize);
|
|
4842
5283
|
};
|
|
4843
5284
|
}, []);
|
|
4844
|
-
if (props.events.length === 0)
|
|
5285
|
+
if (props.events.length === 0) {
|
|
5286
|
+
if (props.state === "loading") return /* @__PURE__ */ React.createElement(DetailNote, { state: "loading" });
|
|
5287
|
+
if (props.state === "failed" && props.onRetry !== void 0) return /* @__PURE__ */ React.createElement(DetailNote, {
|
|
5288
|
+
state: "failed",
|
|
5289
|
+
onRetry: props.onRetry
|
|
5290
|
+
});
|
|
5291
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
|
|
5292
|
+
}
|
|
4845
5293
|
const sorted = props.events.slice().reverse();
|
|
4846
5294
|
return /* @__PURE__ */ React.createElement("div", {
|
|
4847
5295
|
className: "lc-events",
|
|
@@ -4861,6 +5309,7 @@ window.__ModuleLoader__.load({
|
|
|
4861
5309
|
//#region src/client/components/fileCard.tsx
|
|
4862
5310
|
function makeFileCard(kit, settings) {
|
|
4863
5311
|
const { t, fmt, fmtTime } = kit;
|
|
5312
|
+
const DetailNote = makeDetailNote(kit);
|
|
4864
5313
|
function matches(e, f) {
|
|
4865
5314
|
if (f === "all") return true;
|
|
4866
5315
|
if (f === "image") return e.form === "image";
|
|
@@ -4925,7 +5374,10 @@ window.__ModuleLoader__.load({
|
|
|
4925
5374
|
className: "lc-br-err-dot",
|
|
4926
5375
|
title: t("node.failed")
|
|
4927
5376
|
}) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-time" }, op.time !== void 0 ? fmtTime(op.time) : "—"));
|
|
4928
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("files.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, props.scope)), activity.entries.length === 0 ? /* @__PURE__ */ React.createElement(
|
|
5377
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("files.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, props.scope)), activity.entries.length === 0 && props.state === "loading" ? /* @__PURE__ */ React.createElement(DetailNote, { state: "loading" }) : activity.entries.length === 0 && props.state === "failed" && props.onRetry !== void 0 ? /* @__PURE__ */ React.createElement(DetailNote, {
|
|
5378
|
+
state: "failed",
|
|
5379
|
+
onRetry: props.onRetry
|
|
5380
|
+
}) : activity.entries.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("files.empty")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "lc-fa-ctl" }, /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, chips.map((c) => /* @__PURE__ */ React.createElement("button", {
|
|
4929
5381
|
key: c.key,
|
|
4930
5382
|
type: "button",
|
|
4931
5383
|
className: "lc-gran-btn" + (filter === c.key ? " lc-gran-on" : "") + (c.key === "read" || c.key === "write" || c.key === "search" ? " lc-fa-chip-" + c.key : ""),
|
|
@@ -5092,7 +5544,7 @@ window.__ModuleLoader__.load({
|
|
|
5092
5544
|
return function PluginInfo() {
|
|
5093
5545
|
const [latest, setLatest] = React.useState(null);
|
|
5094
5546
|
React.useEffect(() => {
|
|
5095
|
-
if ("0.
|
|
5547
|
+
if ("0.44.0".includes("-dev")) return;
|
|
5096
5548
|
let on = true;
|
|
5097
5549
|
fetchLatestVersion().then((v) => {
|
|
5098
5550
|
if (on && v) setLatest(v);
|
|
@@ -5101,8 +5553,8 @@ window.__ModuleLoader__.load({
|
|
|
5101
5553
|
on = false;
|
|
5102
5554
|
};
|
|
5103
5555
|
}, []);
|
|
5104
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
5105
|
-
const nameText = "dsh-context (v0.
|
|
5556
|
+
const update = latest !== null && isNewerVersion(latest, "0.44.0") ? latest : null;
|
|
5557
|
+
const nameText = "dsh-context (v0.44.0)";
|
|
5106
5558
|
const nameValue = [nameText];
|
|
5107
5559
|
if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
|
|
5108
5560
|
key: "update",
|
|
@@ -5139,19 +5591,7 @@ window.__ModuleLoader__.load({
|
|
|
5139
5591
|
dismissed.add(sessionId);
|
|
5140
5592
|
setClosedFor(sessionId);
|
|
5141
5593
|
}, [sessionId]);
|
|
5142
|
-
|
|
5143
|
-
if (closed) return void 0;
|
|
5144
|
-
const onKey = (ev) => {
|
|
5145
|
-
if (ev.key !== "Escape") return;
|
|
5146
|
-
ev.preventDefault();
|
|
5147
|
-
ev.stopPropagation();
|
|
5148
|
-
close();
|
|
5149
|
-
};
|
|
5150
|
-
window.addEventListener("keydown", onKey, true);
|
|
5151
|
-
return () => {
|
|
5152
|
-
window.removeEventListener("keydown", onKey, true);
|
|
5153
|
-
};
|
|
5154
|
-
}, [closed, close]);
|
|
5594
|
+
useEscapeClose(!closed, close);
|
|
5155
5595
|
if (closed) return null;
|
|
5156
5596
|
return /* @__PURE__ */ React.createElement("div", {
|
|
5157
5597
|
className: "lc-modal-backdrop",
|
|
@@ -5484,19 +5924,32 @@ window.__ModuleLoader__.load({
|
|
|
5484
5924
|
}
|
|
5485
5925
|
//#endregion
|
|
5486
5926
|
//#region src/client/components/statsContext.tsx
|
|
5927
|
+
/**
|
|
5928
|
+
* The inline generation's counter derivation — the exact tally the card ran
|
|
5929
|
+
* over the served collections before the split (distinct turn values, record
|
|
5930
|
+
* count, per-kind event tallies). The host's split-generation counts match
|
|
5931
|
+
* it by construction (fold.ts buildTimelineHead).
|
|
5932
|
+
*/
|
|
5933
|
+
function countsOfRecords(requests, events) {
|
|
5934
|
+
const turns = /* @__PURE__ */ new Set();
|
|
5935
|
+
for (const req of requests) turns.add(req.turn ?? 0);
|
|
5936
|
+
let injects = 0;
|
|
5937
|
+
let compactions = 0;
|
|
5938
|
+
let prunes = 0;
|
|
5939
|
+
for (const ev of events) if (ev.kind === "inject") injects++;
|
|
5940
|
+
else if (ev.kind === "compaction") compactions++;
|
|
5941
|
+
else if (ev.kind === "prune") prunes++;
|
|
5942
|
+
return {
|
|
5943
|
+
turns: turns.size,
|
|
5944
|
+
steps: requests.length,
|
|
5945
|
+
injects,
|
|
5946
|
+
compactions,
|
|
5947
|
+
prunes
|
|
5948
|
+
};
|
|
5949
|
+
}
|
|
5487
5950
|
function makeStatsContext(kit) {
|
|
5488
5951
|
const { t, fmt } = kit;
|
|
5489
5952
|
return function StatsContext(props) {
|
|
5490
|
-
const turns = /* @__PURE__ */ new Set();
|
|
5491
|
-
let steps = 0;
|
|
5492
|
-
for (const req of props.requests) {
|
|
5493
|
-
turns.add(req.turn ?? 0);
|
|
5494
|
-
steps++;
|
|
5495
|
-
}
|
|
5496
|
-
let injects = 0, compactions = 0, prunes = 0;
|
|
5497
|
-
for (const ev of props.events) if (ev.kind === "inject") injects++;
|
|
5498
|
-
else if (ev.kind === "compaction") compactions++;
|
|
5499
|
-
else if (ev.kind === "prune") prunes++;
|
|
5500
5953
|
const currency = props.locale === "zh" ? "cny" : "usd";
|
|
5501
5954
|
const cost = estimateSessionCost(props.cost, currency);
|
|
5502
5955
|
const fmtRate = (n) => formatPriceRate(n, currency);
|
|
@@ -5514,7 +5967,7 @@ window.__ModuleLoader__.load({
|
|
|
5514
5967
|
className: "lc-tip lc-stat-tip",
|
|
5515
5968
|
role: "tooltip"
|
|
5516
5969
|
}, tip));
|
|
5517
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col-stats" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("stats.title"))
|
|
5970
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col-stats" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("stats.title"))), /* @__PURE__ */ React.createElement("div", { className: "lc-stats" }, cell(t("stats.turns"), props.counts.turns), cell(t("stats.steps"), props.counts.steps), cell(t("stats.toolCalls"), props.toolCalls ?? 0), cell(t("stats.images"), props.images ?? 0), cell(t("stats.cost"), cost === null ? "—" : formatCost(cost, currency), costTip), cell(t("stats.injects"), props.counts.injects), cell(t("stats.compactions"), props.counts.compactions), cell(t("stats.prunes"), props.counts.prunes)));
|
|
5518
5971
|
};
|
|
5519
5972
|
}
|
|
5520
5973
|
//#endregion
|
|
@@ -5625,7 +6078,7 @@ window.__ModuleLoader__.load({
|
|
|
5625
6078
|
}
|
|
5626
6079
|
];
|
|
5627
6080
|
}
|
|
5628
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col-stats lc-col-donut" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("timing.title"))
|
|
6081
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col-stats lc-col-donut" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("timing.title"))), rows.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("timing.empty")) : /* @__PURE__ */ React.createElement("div", { className: "lc-donut-row" }, /* @__PURE__ */ React.createElement(Donut, {
|
|
5629
6082
|
segments,
|
|
5630
6083
|
size: 96,
|
|
5631
6084
|
centerTop: wall > 0 ? fmtDuration(wall, lang) : "—",
|
|
@@ -5687,7 +6140,7 @@ window.__ModuleLoader__.load({
|
|
|
5687
6140
|
pct: fmtShare(b.value, billed),
|
|
5688
6141
|
count: b.note === void 0 ? fmt(b.value) : `${fmt(b.value)} · ${b.note}`
|
|
5689
6142
|
}));
|
|
5690
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col-stats lc-col-donut" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("tokens.title"))
|
|
6143
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col-stats lc-col-donut" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("tokens.title"))), /* @__PURE__ */ React.createElement("div", { className: "lc-donut-row" }, /* @__PURE__ */ React.createElement(Donut, {
|
|
5691
6144
|
segments: shown.map((b) => ({
|
|
5692
6145
|
key: b.key,
|
|
5693
6146
|
color: b.color,
|
|
@@ -6146,16 +6599,18 @@ window.__ModuleLoader__.load({
|
|
|
6146
6599
|
const StatsTokens = makeStatsTokens(kit, Donut);
|
|
6147
6600
|
const PluginInfo = makePluginInfo(kit);
|
|
6148
6601
|
const UpgradeGate = makeUpgradeGate(kit);
|
|
6602
|
+
const DetailNote = makeDetailNote(kit);
|
|
6149
6603
|
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
6150
6604
|
const AgentGraph = makeAgentGraph(ctx, kit);
|
|
6151
6605
|
const ErrorBoundary = makeErrorBoundary(t);
|
|
6152
6606
|
function ContextViewBody(props) {
|
|
6153
6607
|
const sessionId = props.sessionId;
|
|
6154
|
-
const
|
|
6155
|
-
const
|
|
6156
|
-
const
|
|
6157
|
-
const
|
|
6158
|
-
const
|
|
6608
|
+
const source = useTimelineSource(ctx, props);
|
|
6609
|
+
const data = source.data;
|
|
6610
|
+
const pressure = projectionOf(props, "contextPressure", contextPressureOf);
|
|
6611
|
+
const usage = projectionOf(props, "tokenUsage", tokenUsageOf);
|
|
6612
|
+
const breakdown = projectionOf(props, "contextBreakdown", contextBreakdownOf);
|
|
6613
|
+
const headers = projectionOf(props, "contextHeaders", headersOf);
|
|
6159
6614
|
const [selectedSeq, setSelectedSeq] = React.useState(null);
|
|
6160
6615
|
const [hoveredSeq, setHoveredSeq] = React.useState(null);
|
|
6161
6616
|
const [hoverTurn, setHoverTurn] = React.useState(null);
|
|
@@ -6211,8 +6666,9 @@ window.__ModuleLoader__.load({
|
|
|
6211
6666
|
const seq = takeContextFocus(sessionId);
|
|
6212
6667
|
if (seq !== null) setJumpSeq(seq);
|
|
6213
6668
|
}, [sessionId]);
|
|
6669
|
+
const detailReady = source.detailState === "ready" || source.detailState === "legacy";
|
|
6214
6670
|
React.useEffect(() => {
|
|
6215
|
-
if (jumpSeq === null || data === null) return;
|
|
6671
|
+
if (jumpSeq === null || data === null || !detailReady) return;
|
|
6216
6672
|
setJumpSeq(null);
|
|
6217
6673
|
const target = jumpTargetOf(aggregateByTurn(requests), jumpSeq);
|
|
6218
6674
|
if (target === null) return;
|
|
@@ -6223,7 +6679,8 @@ window.__ModuleLoader__.load({
|
|
|
6223
6679
|
}, [
|
|
6224
6680
|
jumpSeq,
|
|
6225
6681
|
data,
|
|
6226
|
-
requests
|
|
6682
|
+
requests,
|
|
6683
|
+
detailReady
|
|
6227
6684
|
]);
|
|
6228
6685
|
const briefList = React.useMemo(() => data ? briefNodes(data) : [], [data]);
|
|
6229
6686
|
const convNodes = conversationNodesOf(props);
|
|
@@ -6257,13 +6714,27 @@ window.__ModuleLoader__.load({
|
|
|
6257
6714
|
activeIdx
|
|
6258
6715
|
]);
|
|
6259
6716
|
const convOf = React.useCallback((seq) => bySeq.get(seq), [bySeq]);
|
|
6260
|
-
const fileActivity = React.useMemo(() =>
|
|
6717
|
+
const fileActivity = React.useMemo(() => {
|
|
6718
|
+
if (data !== null && data.fileOps !== void 0) return activityOfOps(data.fileOps, data.archive, filesBefore);
|
|
6719
|
+
return activityOf(briefList, convOf, filesBefore);
|
|
6720
|
+
}, [
|
|
6721
|
+
data,
|
|
6261
6722
|
briefList,
|
|
6262
6723
|
convOf,
|
|
6263
6724
|
filesBefore
|
|
6264
6725
|
]);
|
|
6265
6726
|
const workspace = typeof ctx.get === "function" ? workspaceOf(ctx, typeof sessionId === "string" ? sessionId : void 0) : void 0;
|
|
6266
|
-
const
|
|
6727
|
+
const [canOpenPaths, setCanOpenPaths] = React.useState(false);
|
|
6728
|
+
React.useEffect(() => {
|
|
6729
|
+
let live = true;
|
|
6730
|
+
canOpenPathsOf(ctx).then((can) => {
|
|
6731
|
+
if (live) setCanOpenPaths(can);
|
|
6732
|
+
});
|
|
6733
|
+
return () => {
|
|
6734
|
+
live = false;
|
|
6735
|
+
};
|
|
6736
|
+
}, [ctx]);
|
|
6737
|
+
const fileOpener = React.useMemo(() => canOpenPaths ? openPathVia(ctx) : void 0, [canOpenPaths, ctx]);
|
|
6267
6738
|
const locateFileOp = React.useCallback((op) => {
|
|
6268
6739
|
const seq = op.parent ?? op.seq;
|
|
6269
6740
|
const step = locateStepOf(requests, seq, op.gone);
|
|
@@ -6325,8 +6796,7 @@ window.__ModuleLoader__.load({
|
|
|
6325
6796
|
className: "lc-root",
|
|
6326
6797
|
ref: rootRef
|
|
6327
6798
|
}, /* @__PURE__ */ React.createElement("div", { className: "lc-cols lc-head" }, /* @__PURE__ */ React.createElement(StatsContext, {
|
|
6328
|
-
requests,
|
|
6329
|
-
events,
|
|
6799
|
+
counts: data.counts ?? countsOfRecords(requests, events),
|
|
6330
6800
|
toolCalls: data.toolCalls,
|
|
6331
6801
|
images: data.images,
|
|
6332
6802
|
cost: data.cost,
|
|
@@ -6362,7 +6832,10 @@ window.__ModuleLoader__.load({
|
|
|
6362
6832
|
onClick: () => {
|
|
6363
6833
|
setTrendMode("delta");
|
|
6364
6834
|
}
|
|
6365
|
-
}, t("gran.delta"))))), displayRequests.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("trend.empty")) : /* @__PURE__ */ React.createElement(
|
|
6835
|
+
}, t("gran.delta"))))), displayRequests.length === 0 ? detailReady ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("trend.empty")) : /* @__PURE__ */ React.createElement(DetailNote, {
|
|
6836
|
+
state: source.detailState === "failed" ? "failed" : "loading",
|
|
6837
|
+
onRetry: source.retryDetail
|
|
6838
|
+
}) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(TrendChart, {
|
|
6366
6839
|
key: sessionId,
|
|
6367
6840
|
requests: displayRequests,
|
|
6368
6841
|
markers,
|
|
@@ -6406,19 +6879,27 @@ window.__ModuleLoader__.load({
|
|
|
6406
6879
|
onHoverKey: setHoverCat,
|
|
6407
6880
|
nodeFocus,
|
|
6408
6881
|
onNodeFocusHandled: clearNodeFocus,
|
|
6409
|
-
loadImage
|
|
6882
|
+
loadImage,
|
|
6883
|
+
detailState: source.detailState,
|
|
6884
|
+
onDetailRetry: source.retryDetail
|
|
6410
6885
|
}))), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("events.title")), /* @__PURE__ */ React.createElement("div", { className: "lc-kinds" }, EVENT_KINDS.map((k) => /* @__PURE__ */ React.createElement("button", {
|
|
6411
6886
|
key: k,
|
|
6412
6887
|
className: "lc-gran-btn" + (pickedKinds.includes(k) ? " lc-gran-on lc-kind-" + k : ""),
|
|
6413
6888
|
onClick: () => {
|
|
6414
6889
|
toggleKind(k);
|
|
6415
6890
|
}
|
|
6416
|
-
}, t("kind." + k))))), /* @__PURE__ */ React.createElement(EventList, {
|
|
6891
|
+
}, t("kind." + k))))), /* @__PURE__ */ React.createElement(EventList, {
|
|
6892
|
+
events: shownEvents,
|
|
6893
|
+
state: source.detailState,
|
|
6894
|
+
onRetry: source.retryDetail
|
|
6895
|
+
})), /* @__PURE__ */ React.createElement(FileCard, {
|
|
6417
6896
|
activity: fileActivity,
|
|
6418
6897
|
scope: fileScope,
|
|
6419
6898
|
workspace,
|
|
6420
6899
|
onOpen: fileOpener,
|
|
6421
|
-
onLocate: locateFileOp
|
|
6900
|
+
onLocate: locateFileOp,
|
|
6901
|
+
state: source.detailState,
|
|
6902
|
+
onRetry: source.retryDetail
|
|
6422
6903
|
})), /* @__PURE__ */ React.createElement(AgentGraph, {
|
|
6423
6904
|
sessionId: typeof sessionId === "string" ? sessionId : void 0,
|
|
6424
6905
|
self: {
|
|
@@ -6623,7 +7104,7 @@ window.__ModuleLoader__.load({
|
|
|
6623
7104
|
t,
|
|
6624
7105
|
fmt,
|
|
6625
7106
|
fmtTime,
|
|
6626
|
-
fmtDuration
|
|
7107
|
+
fmtDuration,
|
|
6627
7108
|
fmtShare,
|
|
6628
7109
|
catLabel: (key) => t("cat." + key),
|
|
6629
7110
|
eventLabel,
|
|
@@ -6632,7 +7113,7 @@ window.__ModuleLoader__.load({
|
|
|
6632
7113
|
}
|
|
6633
7114
|
//#endregion
|
|
6634
7115
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/base.css.mjs
|
|
6635
|
-
const css$13 = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-root .lc-card{margin-bottom:7px;padding:7px 8px}.lc-root .lc-cols{gap:7px;margin-bottom:7px}.lc-card-title{flex-wrap:wrap;align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3,0 2px 8px #0000002e);pointer-events:none;opacity:0;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:6px 10px;font-size:12px;position:absolute}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;height:320px;display:flex;overflow-y:auto}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card{flex:1 1 0;min-width:0
|
|
7116
|
+
const css$13 = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-root .lc-card{margin-bottom:7px;padding:7px 8px;container:lc-card/inline-size}.lc-root .lc-cols{gap:7px;margin-bottom:7px}.lc-card-title{flex-wrap:wrap;align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3,0 2px 8px #0000002e);pointer-events:none;opacity:0;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:6px 10px;font-size:12px;position:absolute}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;height:320px;display:flex;overflow-y:auto}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:min(280px,100%)}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head{container:lc-head-row/inline-size}.lc-head>.lc-card{flex:1 1 0;min-width:0}@container lc-head-row (width<=1045px){.lc-head>.lc-card{flex-basis:40%}}@container lc-head-row (width<=480px){.lc-head>.lc-card{flex-basis:100%}}.lc-head>.lc-card:first-child{flex-direction:column;display:flex}.lc-head>.lc-card:first-child .lc-stats{flex:1;align-content:stretch}.lc-head>.lc-card:first-child .lc-stat{justify-content:center}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])),[data-conversation-scroll]:has(.lc-root,.lc-modal-backdrop)~[data-width-handle]{display:none}";
|
|
6636
7117
|
const tagId$13 = "dsh-context/base.css";
|
|
6637
7118
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
|
|
6638
7119
|
const tag = document.createElement("style");
|
|
@@ -6643,7 +7124,7 @@ window.__ModuleLoader__.load({
|
|
|
6643
7124
|
}
|
|
6644
7125
|
//#endregion
|
|
6645
7126
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/stats.css.mjs
|
|
6646
|
-
const css$12 = ".lc-stats{grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-donut-row{justify-content:flex-start;align-items:center;gap:12px;min-width:0;display:flex}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut-row .lc-sl{flex:auto}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:#8080802e}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{flex-direction:column;gap:6px;min-width:0;display:flex;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-hint{text-decoration:none}.lc-pi-hint:hover{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-pi-row-btn .lc-pi-value{font-weight:400}@container lc-
|
|
7127
|
+
const css$12 = ".lc-stats{grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-donut-row{justify-content:flex-start;align-items:center;gap:12px;min-width:0;display:flex}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut-row .lc-sl{flex:auto}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:#8080802e}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{flex-direction:column;gap:6px;min-width:0;display:flex;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-hint{text-decoration:none}.lc-pi-hint:hover{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-pi-row-btn .lc-pi-value{font-weight:400}@container lc-card (width<=320px){.lc-donut-row{gap:8px}.lc-stats{grid-template-columns:repeat(2,minmax(0,1fr))}}@container lc-card (width<=240px){.lc-donut-row{flex-wrap:wrap}.lc-donut{margin:0 auto}.lc-donut-row .lc-sl{flex-basis:100%}}";
|
|
6647
7128
|
const tagId$12 = "dsh-context/stats.css";
|
|
6648
7129
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$12) + "]") === null) {
|
|
6649
7130
|
const tag = document.createElement("style");
|
|
@@ -6709,7 +7190,7 @@ window.__ModuleLoader__.load({
|
|
|
6709
7190
|
}
|
|
6710
7191
|
//#endregion
|
|
6711
7192
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/events.css.mjs
|
|
6712
|
-
const css$6 = ".lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}";
|
|
7193
|
+
const css$6 = ".lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}@container lc-card (width<=380px){.lc-event{flex-wrap:wrap}.lc-event-label{flex-basis:calc(100% - 92px)}}";
|
|
6713
7194
|
const tagId$6 = "dsh-context/events.css";
|
|
6714
7195
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$6) + "]") === null) {
|
|
6715
7196
|
const tag = document.createElement("style");
|
|
@@ -6720,7 +7201,7 @@ window.__ModuleLoader__.load({
|
|
|
6720
7201
|
}
|
|
6721
7202
|
//#endregion
|
|
6722
7203
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/fileCard.css.mjs
|
|
6723
|
-
const css$5 = ".lc-fa-ctl,.lc-br-toolctl{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px;display:flex}.lc-fa-ctl .lc-gran{margin-left:0}.lc-fa-n{opacity:.75;margin-left:4px;font-weight:700}.lc-fa-search,.lc-br-tool-search{min-width:80px;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;outline:none;flex:1;padding:3px 8px;font-size:12px}.lc-fa-search:focus,.lc-br-tool-search:focus{border-color:var(--dsw-alias-label-dimmed)}.lc-fa-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:6px;font-size:12px;display:flex}.lc-fa-meta .lc-fa-sort{flex:none}.lc-fa-meta-delta{position:relative}.lc-fa-meta-tip{white-space:normal;max-width:260px;line-height:1.5;top:calc(100% + 6px);left:0}.lc-fa-meta-delta:hover .lc-fa-meta-tip{opacity:1}.lc-fa-item{transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border:1px solid #0000;border-radius:8px}.lc-fa-item-on{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l2)}.lc-fa-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:7px;align-items:center;gap:7px;padding:6px 8px;font-size:12px;display:flex}.lc-fa-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-form{opacity:.85;flex:none;justify-content:center;align-items:center;width:20px;height:14px;font-size:11px;display:flex}.lc-fa-lang{border-radius:4px;justify-content:center;align-items:center;min-width:16px;height:13px;padding:0 2px;font-size:8px;font-weight:700;line-height:1;display:inline-flex}.lc-fa-row .lc-br-chev{margin-right:-2px}.lc-fa-path{word-break:break-all;flex:1;min-width:0}.lc-fa-file{cursor:pointer}.lc-fa-file:hover{text-decoration:underline}.lc-fa-path em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-fa-badge{font-variant-numeric:tabular-nums;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:1px 7px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-badge i{background:currentColor;border-radius:50%;width:5px;height:5px;display:inline-block}.lc-fa-b-read{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent)}.lc-fa-b-write{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent)}.lc-fa-b-search{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent)}.lc-fa-chip-read,.lc-fa-chip-read:hover{color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write,.lc-fa-chip-write:hover{color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search,.lc-fa-chip-search:hover{color:var(--dsw-alias-state-warn-primary)}.lc-fa-chip-read.lc-gran-on,.lc-fa-chip-read.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write.lc-gran-on,.lc-fa-chip-write.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search.lc-gran-on,.lc-fa-chip-search.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);color:var(--dsw-alias-state-warn-primary)}.lc-fa-delta{font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;gap:5px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-up{color:var(--dsw-alias-state-success-primary)}.lc-fa-down{color:var(--dsw-alias-state-error-primary)}.lc-fa-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.lc-fa-ops{border-left:2px solid var(--dsw-alias-border-l1);flex-direction:column;gap:1px;margin:0 8px 8px 15px;padding:2px 0 2px 10px;display:flex}.lc-fa-op{min-width:0;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:5px;align-items:center;gap:8px;padding:3px 6px;font-size:11px;display:flex}.lc-fa-op-link{cursor:pointer}.lc-fa-op-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-op-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto}.lc-fa-op-tool{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);max-width:220px;color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-fa-op-detail{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;overflow:hidden}.lc-fa-read{color:var(--dsw-alias-brand-primary);font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;font-size:11px;font-weight:600}";
|
|
7204
|
+
const css$5 = ".lc-fa-ctl,.lc-br-toolctl{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px;display:flex}.lc-fa-ctl .lc-gran{margin-left:0}.lc-fa-n{opacity:.75;margin-left:4px;font-weight:700}.lc-fa-search,.lc-br-tool-search{min-width:80px;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;outline:none;flex:1;padding:3px 8px;font-size:12px}.lc-fa-search:focus,.lc-br-tool-search:focus{border-color:var(--dsw-alias-label-dimmed)}.lc-fa-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:6px;font-size:12px;display:flex}.lc-fa-meta .lc-fa-sort{flex:none}.lc-fa-meta-delta{position:relative}.lc-fa-meta-tip{white-space:normal;max-width:260px;line-height:1.5;top:calc(100% + 6px);left:0}.lc-fa-meta-delta:hover .lc-fa-meta-tip{opacity:1}.lc-fa-item{transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border:1px solid #0000;border-radius:8px}.lc-fa-item-on{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l2)}.lc-fa-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:7px;align-items:center;gap:7px;padding:6px 8px;font-size:12px;display:flex}.lc-fa-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-form{opacity:.85;flex:none;justify-content:center;align-items:center;width:20px;height:14px;font-size:11px;display:flex}.lc-fa-lang{border-radius:4px;justify-content:center;align-items:center;min-width:16px;height:13px;padding:0 2px;font-size:8px;font-weight:700;line-height:1;display:inline-flex}.lc-fa-row .lc-br-chev{margin-right:-2px}.lc-fa-path{word-break:break-all;flex:1;min-width:0}@container lc-card (width<=380px){.lc-fa-row{flex-wrap:wrap}.lc-fa-path{flex-basis:calc(100% - 46px)}.lc-fa-ctl .lc-gran{flex-wrap:wrap}}.lc-fa-file{cursor:pointer}.lc-fa-file:hover{text-decoration:underline}.lc-fa-path em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-fa-badge{font-variant-numeric:tabular-nums;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:1px 7px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-badge i{background:currentColor;border-radius:50%;width:5px;height:5px;display:inline-block}.lc-fa-b-read{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent)}.lc-fa-b-write{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent)}.lc-fa-b-search{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent)}.lc-fa-chip-read,.lc-fa-chip-read:hover{color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write,.lc-fa-chip-write:hover{color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search,.lc-fa-chip-search:hover{color:var(--dsw-alias-state-warn-primary)}.lc-fa-chip-read.lc-gran-on,.lc-fa-chip-read.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write.lc-gran-on,.lc-fa-chip-write.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search.lc-gran-on,.lc-fa-chip-search.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);color:var(--dsw-alias-state-warn-primary)}.lc-fa-delta{font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;gap:5px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-up{color:var(--dsw-alias-state-success-primary)}.lc-fa-down{color:var(--dsw-alias-state-error-primary)}.lc-fa-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.lc-fa-ops{border-left:2px solid var(--dsw-alias-border-l1);flex-direction:column;gap:1px;margin:0 8px 8px 15px;padding:2px 0 2px 10px;display:flex}.lc-fa-op{min-width:0;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:5px;align-items:center;gap:8px;padding:3px 6px;font-size:11px;display:flex}.lc-fa-op-link{cursor:pointer}.lc-fa-op-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-op-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto}.lc-fa-op-tool{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);max-width:220px;color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-fa-op-detail{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;overflow:hidden}.lc-fa-read{color:var(--dsw-alias-brand-primary);font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;font-size:11px;font-weight:600}";
|
|
6724
7205
|
const tagId$5 = "dsh-context/fileCard.css";
|
|
6725
7206
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
|
|
6726
7207
|
const tag = document.createElement("style");
|
|
@@ -6794,12 +7275,13 @@ window.__ModuleLoader__.load({
|
|
|
6794
7275
|
* context-composition timeline: current makeup, per-request stacked-bar
|
|
6795
7276
|
* history, context events, and the live message list.
|
|
6796
7277
|
*
|
|
6797
|
-
* Since v0.9 the tab
|
|
6798
|
-
*
|
|
6799
|
-
* (`contextTimeline
|
|
6800
|
-
*
|
|
6801
|
-
*
|
|
6802
|
-
*
|
|
7278
|
+
* Since v0.9 the tab rides the harness's session-projection pipeline
|
|
7279
|
+
* (`contextTimeline` projection key), read from the framework standard kit
|
|
7280
|
+
* (`useProjection('contextTimeline')`, a standard prop on every session-scope
|
|
7281
|
+
* slot component). The wire value is the split generation's slim head; the
|
|
7282
|
+
* heavy collections arrive on demand from the host's detail endpoint, one
|
|
7283
|
+
* read per viewing client (timelineSource.ts). No polling, no stale-while-
|
|
7284
|
+
* revalidate cache.
|
|
6803
7285
|
*
|
|
6804
7286
|
* This module is the body of the package's `./client` bundle: tsdown
|
|
6805
7287
|
* (tsdown.config.ts) bundles it (external `react` — the browser module table
|