claude-discover 1.9.0 → 1.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/proxy.mjs +14 -1
- package/out/main/main.js +371 -65
- package/out/preload/preload.cjs +3 -1
- package/out/renderer/assets/{index-0bBLuYQv.css → index-03LlBvFr.css} +27 -1
- package/out/renderer/assets/{index-BLwpO6Wp.js → index-BugPzqD2.js} +161 -37
- package/out/renderer/assets/vendors-S1fS5xz9.js +52139 -0
- package/out/renderer/index.html +3 -3
- package/package.json +5 -3
- package/out/renderer/assets/vendors-D6iOvAG3.js +0 -49248
|
@@ -557,8 +557,11 @@
|
|
|
557
557
|
&:hover { background: var(--bg-elev-2); }
|
|
558
558
|
&.selected { background: var(--selected); }
|
|
559
559
|
&.is-subagent-child { padding-left: 20px; }
|
|
560
|
+
&.deep-linked { background: rgba(52, 211, 153, 0.12); } /* last, so it wins over :hover and .selected */
|
|
560
561
|
}
|
|
561
562
|
|
|
563
|
+
.deeplink-note { color: #34d399; }
|
|
564
|
+
|
|
562
565
|
.session-row-main { flex: 1; min-width: 0; }
|
|
563
566
|
|
|
564
567
|
.session-row-stats {
|
|
@@ -733,9 +736,9 @@
|
|
|
733
736
|
}
|
|
734
737
|
|
|
735
738
|
.conv-row + .conv-row { margin-top: 15px; }
|
|
739
|
+
/* One instruction row per API call (see groupTurns) — the gap keeps two calls' strips apart. */
|
|
736
740
|
.conv-row-instruction + .conv-row { margin-top: 20px; }
|
|
737
741
|
.conv-row-assistant + .conv-row-user { margin-top: 60px; }
|
|
738
|
-
.conv-row-instruction + .conv-row-instruction,
|
|
739
742
|
.conv-row-assistant + .conv-row-assistant { margin-top: 0; }
|
|
740
743
|
|
|
741
744
|
|
|
@@ -1077,6 +1080,18 @@ button.msg-summary:hover { color: var(--text); background: var(--bg-elev-2); bor
|
|
|
1077
1080
|
margin-left: -10px;
|
|
1078
1081
|
}
|
|
1079
1082
|
|
|
1083
|
+
/* Full-width toggle so the estimated token count can sit right-aligned, like msg-header-right. */
|
|
1084
|
+
.instruction-file .aux-toggle { display: flex; width: 100%; align-items: center; }
|
|
1085
|
+
.instruction-tokens { margin-left: auto; color: var(--text-faint); }
|
|
1086
|
+
|
|
1087
|
+
/* Summed token total under a run of instruction files: a rule over the right-aligned figure. */
|
|
1088
|
+
.instruction-total { display: flex; font-size: calc(11px * var(--font-scale, 1)); }
|
|
1089
|
+
.instruction-total .instruction-tokens {
|
|
1090
|
+
border-top: 1px solid var(--border);
|
|
1091
|
+
padding-top: 2px;
|
|
1092
|
+
font-weight: 600;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1080
1095
|
/* Full path of an expanded memory file, first line of the body — one click selects it for copying. */
|
|
1081
1096
|
.instruction-path {
|
|
1082
1097
|
font-family: var(--font-mono), monospace;
|
|
@@ -1814,6 +1829,17 @@ button.msg-summary:hover { color: var(--text); background: var(--bg-elev-2); bor
|
|
|
1814
1829
|
}
|
|
1815
1830
|
}
|
|
1816
1831
|
|
|
1832
|
+
.session-missing {
|
|
1833
|
+
max-width: 520px;
|
|
1834
|
+
padding: 14px 18px;
|
|
1835
|
+
border: 1px solid rgba(251, 191, 36, 0.35);
|
|
1836
|
+
border-radius: 6px;
|
|
1837
|
+
background: rgba(251, 191, 36, 0.08);
|
|
1838
|
+
color: var(--warn);
|
|
1839
|
+
text-align: center;
|
|
1840
|
+
word-break: break-word;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1817
1843
|
.view-body {
|
|
1818
1844
|
flex: 1;
|
|
1819
1845
|
display: flex;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as reactExports, j as jsxRuntimeExports, f as format, s as startOfWeek, a as startOfMonth, b as startOfDay, e as endOfWeek, c as endOfMonth, d as endOfDay, g as addWeeks, h as addMonths, i as addDays, k as isSameWeek, l as isSameMonth, m as isSameDay, n as intervalToDuration, o as getDaysInMonth, p as addHours, H as Hourglass, G as Gauge, M as MessageSquare, C as Clock, Z as Zap, D as DollarSign, R as React,
|
|
1
|
+
import { r as reactExports, j as jsxRuntimeExports, f as format, s as startOfWeek, a as startOfMonth, b as startOfDay, e as endOfWeek, c as endOfMonth, d as endOfDay, g as addWeeks, h as addMonths, i as addDays, k as isSameWeek, l as isSameMonth, m as isSameDay, n as intervalToDuration, o as getDaysInMonth, p as addHours, q as encode, H as Hourglass, G as Gauge, M as MessageSquare, C as Clock, Z as Zap, D as DollarSign, R as React, t as Markdown$1, u as rehypeHighlight, v as remarkGfm, T as Terminal, J as JsonView, w as vscodeTheme, x as tippy, y as reactDomExports, z as sn, X as Xt, A as Zt, B as tn, E as parse, F as delegate, I as clientExports } from "./vendors-S1fS5xz9.js";
|
|
2
2
|
const SOURCE_COLORS = {
|
|
3
3
|
scheduled: "#14b8a6",
|
|
4
4
|
cli: "#3b82f6",
|
|
@@ -34,13 +34,13 @@ function useLocalStorage(key, initial) {
|
|
|
34
34
|
}
|
|
35
35
|
function clearOutdatedLocalStorage() {
|
|
36
36
|
const version = localStorage.getItem(VERSION_KEY);
|
|
37
|
-
if (version !== "1.9.
|
|
38
|
-
const [MAJOR, MINOR, PATCH] = "1.9.
|
|
37
|
+
if (version !== "1.9.2") {
|
|
38
|
+
const [MAJOR, MINOR, PATCH] = "1.9.2".split(".");
|
|
39
39
|
const [major, minor, patch] = (version ?? "").split(".");
|
|
40
40
|
if (MAJOR !== major || MINOR !== minor) {
|
|
41
41
|
localStorage.clear();
|
|
42
42
|
}
|
|
43
|
-
localStorage.setItem(VERSION_KEY, "1.9.
|
|
43
|
+
localStorage.setItem(VERSION_KEY, "1.9.2");
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
function useMouseFontScale(storageKey) {
|
|
@@ -721,8 +721,8 @@ function tone(value, { warn, danger }) {
|
|
|
721
721
|
function fmtCompact(n) {
|
|
722
722
|
if (n == null) return "—";
|
|
723
723
|
if (n < 0) return `-${fmtCompact(-n)}`;
|
|
724
|
-
if (n <
|
|
725
|
-
if (n < 1e6) return `${
|
|
724
|
+
if (n < 100) return `${n}`;
|
|
725
|
+
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
726
726
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
727
727
|
}
|
|
728
728
|
const BAR_MAX_PX = 52;
|
|
@@ -872,10 +872,34 @@ function PeriodSummary({ sessions, dayAnchor, granularity = "day" }) {
|
|
|
872
872
|
] })
|
|
873
873
|
] });
|
|
874
874
|
}
|
|
875
|
+
const countTokens = (text, model = "") => {
|
|
876
|
+
if (!text) return 0;
|
|
877
|
+
const LEGACY_CLAUDE = /-(3|4)(-|$)/;
|
|
878
|
+
const scale = LEGACY_CLAUDE.test(model) ? 1.13 : 1.5;
|
|
879
|
+
return Math.round(encode(text).length * scale);
|
|
880
|
+
};
|
|
881
|
+
const queuedPrompt = (it) => it.type === "attachment" && it.attachment?.type === "queued_command" && it.attachment.commandMode === "prompt" && (it.attachment.origin?.kind ?? "human") === "human" && it.attachment.prompt?.trim() ? it.attachment.prompt : null;
|
|
875
882
|
function flatten(items, instructions = []) {
|
|
876
883
|
const turns = [];
|
|
877
884
|
const results = {};
|
|
878
885
|
for (const it of items) {
|
|
886
|
+
const queued = queuedPrompt(it);
|
|
887
|
+
if (queued) {
|
|
888
|
+
turns.push({
|
|
889
|
+
uuid: it.uuid,
|
|
890
|
+
role: "user",
|
|
891
|
+
isMeta: false,
|
|
892
|
+
queued: true,
|
|
893
|
+
ts: it.timestamp ? Date.parse(it.timestamp) : null,
|
|
894
|
+
model: null,
|
|
895
|
+
msgId: null,
|
|
896
|
+
usage: null,
|
|
897
|
+
tokenDelta: it._tokenDelta ?? null,
|
|
898
|
+
tokenTotal: it._tokenTotal ?? null,
|
|
899
|
+
blocks: [{ type: "text", text: queued }]
|
|
900
|
+
});
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
879
903
|
if (it.type === "attachment" && it.attachment) {
|
|
880
904
|
const block = { type: "attachment", attachment: it.attachment };
|
|
881
905
|
const last = turns[turns.length - 1];
|
|
@@ -959,6 +983,26 @@ ${text}` }]
|
|
|
959
983
|
return out;
|
|
960
984
|
}
|
|
961
985
|
const currentModel = (turns) => turns.findLast((t) => t.model && t.model !== "<synthetic>")?.model;
|
|
986
|
+
const SMALL_CONTEXT = /* @__PURE__ */ new Set([
|
|
987
|
+
"claude-3-opus",
|
|
988
|
+
"claude-3-sonnet",
|
|
989
|
+
"claude-3-haiku",
|
|
990
|
+
"claude-3-5-sonnet",
|
|
991
|
+
"claude-3-5-haiku",
|
|
992
|
+
"claude-3-7-sonnet",
|
|
993
|
+
"claude-haiku-4-5",
|
|
994
|
+
"claude-opus-4",
|
|
995
|
+
"claude-opus-4-1",
|
|
996
|
+
"claude-opus-4-5",
|
|
997
|
+
"claude-opus-4-6",
|
|
998
|
+
"claude-sonnet-4",
|
|
999
|
+
"claude-sonnet-4-5",
|
|
1000
|
+
"claude-sonnet-4-6"
|
|
1001
|
+
]);
|
|
1002
|
+
function contextWindow(models = []) {
|
|
1003
|
+
const is200k = (model) => SMALL_CONTEXT.has(model.replace(/[-@]\d{8}.*$/, ""));
|
|
1004
|
+
return models.some((m) => m && !is200k(m)) ? 1e6 : 2e5;
|
|
1005
|
+
}
|
|
962
1006
|
function normalizeContent(content) {
|
|
963
1007
|
if (typeof content === "string") return [{ type: "text", text: content }];
|
|
964
1008
|
if (!Array.isArray(content)) return [];
|
|
@@ -1027,7 +1071,9 @@ function groupTurns(turns) {
|
|
|
1027
1071
|
else pending.push(t);
|
|
1028
1072
|
} else if (t.role === "instruction") {
|
|
1029
1073
|
flush();
|
|
1030
|
-
groups.
|
|
1074
|
+
const prev = groups.at(-1);
|
|
1075
|
+
if (prev?.kind === "instruction" && prev.turns.at(-1).ts === t.ts) prev.turns.push(t);
|
|
1076
|
+
else groups.push({ kind: "instruction", turns: [t] });
|
|
1031
1077
|
} else if (t.role === "user") {
|
|
1032
1078
|
flush();
|
|
1033
1079
|
groups.push({ kind: "user", turns: [...pending, t] });
|
|
@@ -1045,7 +1091,7 @@ function groupTurns(turns) {
|
|
|
1045
1091
|
function cycleDurations(groups) {
|
|
1046
1092
|
let prevEnd = null;
|
|
1047
1093
|
return groups.map((g) => {
|
|
1048
|
-
const tss =
|
|
1094
|
+
const tss = g.turns.map((t) => t.ts).filter((ts) => ts != null);
|
|
1049
1095
|
const end = tss.length ? tss[tss.length - 1] : null;
|
|
1050
1096
|
const duration2 = g.kind === "assistant" && end != null && prevEnd != null ? end - prevEnd : null;
|
|
1051
1097
|
if (end != null) prevEnd = end;
|
|
@@ -1055,7 +1101,7 @@ function cycleDurations(groups) {
|
|
|
1055
1101
|
function tokenPoints(groups) {
|
|
1056
1102
|
let prev = 0;
|
|
1057
1103
|
return groups.map((g) => {
|
|
1058
|
-
const turns = g.turns
|
|
1104
|
+
const turns = g.turns;
|
|
1059
1105
|
let total = null, ctx = null;
|
|
1060
1106
|
const byMsg = /* @__PURE__ */ new Map();
|
|
1061
1107
|
for (const t of turns) {
|
|
@@ -1076,7 +1122,7 @@ function tokenPoints(groups) {
|
|
|
1076
1122
|
usage.cacheCreation5m += u.cache_creation?.ephemeral_5m_input_tokens || 0;
|
|
1077
1123
|
usage.cacheCreation1h += u.cache_creation?.ephemeral_1h_input_tokens || 0;
|
|
1078
1124
|
}
|
|
1079
|
-
if (total == null || total
|
|
1125
|
+
if (total == null || total <= prev) return null;
|
|
1080
1126
|
const delta = total - prev;
|
|
1081
1127
|
prev = total;
|
|
1082
1128
|
if (ctx == null && g.kind !== "assistant") ctx = delta;
|
|
@@ -1090,7 +1136,7 @@ const TIPS = {
|
|
|
1090
1136
|
messages: `Consider sessions below ${fmtCompact(THRESHOLDS.messages.warn)} messages`,
|
|
1091
1137
|
time: `Consider sessions under ${THRESHOLDS.workTime.warn / 6e4}m`
|
|
1092
1138
|
};
|
|
1093
|
-
function SessionList({ sessions, selectedId, onSelect }) {
|
|
1139
|
+
function SessionList({ sessions, selectedId, deepLink, onSelect }) {
|
|
1094
1140
|
const selectedRef = reactExports.useRef(null);
|
|
1095
1141
|
const [sortBy, setSortBy] = useLocalStorage("session-list.sort", "time");
|
|
1096
1142
|
const [filter, setFilter] = reactExports.useState("");
|
|
@@ -1165,12 +1211,15 @@ function SessionList({ sessions, selectedId, onSelect }) {
|
|
|
1165
1211
|
const isSubagent = s.sessionId.startsWith("agent-");
|
|
1166
1212
|
const journal = isJournal(s);
|
|
1167
1213
|
const isFork = !!s.forkedFrom;
|
|
1214
|
+
const linked = deepLink?.id === s.sessionId;
|
|
1215
|
+
const linkTip = linked && `Opened from deep link
|
|
1216
|
+
claude-discover://session?id=${deepLink.id}${deepLink.date ? `&date=${deepLink.date}` : ""}`;
|
|
1168
1217
|
const sessionName = s.customTitle && s.agentName && s.customTitle !== s.agentName ? `${s.customTitle} [${s.agentName}]` : s.customTitle || s.agentName || "";
|
|
1169
1218
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1170
1219
|
"div",
|
|
1171
1220
|
{
|
|
1172
1221
|
ref: selectedId === s.sessionId ? selectedRef : null,
|
|
1173
|
-
className: `session-row ${selectedId === s.sessionId ? "selected" : ""} ${isChild ? "is-subagent-child" : ""}`,
|
|
1222
|
+
className: `session-row ${selectedId === s.sessionId ? "selected" : ""} ${isChild ? "is-subagent-child" : ""} ${linked ? "deep-linked" : ""}`,
|
|
1174
1223
|
onClick: () => onSelect(selectedId === s.sessionId ? null : s.sessionId),
|
|
1175
1224
|
style: { borderLeftColor: SOURCE_COLORS[s.source] || SOURCE_COLORS.other },
|
|
1176
1225
|
children: [
|
|
@@ -1214,7 +1263,8 @@ ${TIPS.time}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { size: 11
|
|
|
1214
1263
|
s.models?.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "session-model", children: [
|
|
1215
1264
|
", ",
|
|
1216
1265
|
s.models.map((m) => m.replace(/^claude-/, "")).join(", ")
|
|
1217
|
-
] })
|
|
1266
|
+
] }),
|
|
1267
|
+
linked && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "deeplink-note", "data-tippy-content": linkTip, "data-tippy-maxWidth": 560, children: ", opened from deep link" })
|
|
1218
1268
|
] })
|
|
1219
1269
|
] }),
|
|
1220
1270
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "session-row-stats", children: [
|
|
@@ -1381,22 +1431,23 @@ function ConversationView({ items, instructions = [], expandAll = null }) {
|
|
|
1381
1431
|
const durations = reactExports.useMemo(() => cycleDurations(groups), [groups]);
|
|
1382
1432
|
const findOpen = useFindActive();
|
|
1383
1433
|
const [scale, zoomRef] = useMouseFontScale("font-scale.conversation");
|
|
1434
|
+
const model = currentModel(turns);
|
|
1435
|
+
const ctxLimit = contextWindow([model]);
|
|
1384
1436
|
const hasTimeline = points.some(Boolean);
|
|
1385
1437
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandAllContext.Provider, { value: expandAll, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: zoomRef, className: `conversation${hasTimeline ? " has-token-timeline" : ""}`, style: { "--font-scale": scale }, children: groups.map((g, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `conv-row conv-row-${g.kind}`, children: [
|
|
1386
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(LazyMount, { eager: i < 8, forceMount: findOpen, placeholderMinHeight: 80, children: g.kind === "user" ? /* @__PURE__ */ jsxRuntimeExports.jsx(UserRow, { turns: g.turns, point: points[i] }) : g.kind === "assistant" ? /* @__PURE__ */ jsxRuntimeExports.jsx(AssistantCard, { turns: g.turns, point: points[i], duration: durations[i], showAuthor: groups[i - 1]?.kind !== "assistant" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1438
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(LazyMount, { eager: i < 8, forceMount: findOpen, placeholderMinHeight: 80, children: g.kind === "user" ? /* @__PURE__ */ jsxRuntimeExports.jsx(UserRow, { turns: g.turns, point: points[i], ctxLimit }) : g.kind === "assistant" ? /* @__PURE__ */ jsxRuntimeExports.jsx(AssistantCard, { turns: g.turns, point: points[i], ctxLimit, duration: durations[i], showAuthor: groups[i - 1]?.kind !== "assistant" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(InstructionRun, { turns: g.turns, model }) }),
|
|
1387
1439
|
points[i] ? /* @__PURE__ */ jsxRuntimeExports.jsx(TokenPoint, { point: points[i] }) : hasTimeline && g.kind === "user" && g.turns.some(isCommandTurn) && /* @__PURE__ */ jsxRuntimeExports.jsx(TokenPoint, { point: { role: "command", delta: 0 } })
|
|
1388
|
-
] }, g.
|
|
1440
|
+
] }, g.turns[0].uuid)) }) });
|
|
1389
1441
|
}
|
|
1390
1442
|
const ClaudeIcon = () => /* @__PURE__ */ jsxRuntimeExports.jsx("img", { className: "msg-icon claude-icon", src: claudeIcon, alt: "" });
|
|
1391
|
-
const CTX_LIMIT = THRESHOLDS.context.danger;
|
|
1392
1443
|
function TokenPoint({ point: p }) {
|
|
1393
1444
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `token-point token-point-${p.role}${p.delta < 0 ? " negative" : ""}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "token-point-dot" }) });
|
|
1394
1445
|
}
|
|
1395
|
-
function TokenStats({ point }) {
|
|
1446
|
+
function TokenStats({ point, ctxLimit }) {
|
|
1396
1447
|
if (!point) return null;
|
|
1397
1448
|
const rows = [];
|
|
1398
1449
|
if (point.ctx != null) {
|
|
1399
|
-
const width = Math.min(point.ctx /
|
|
1450
|
+
const width = Math.min(point.ctx / ctxLimit, 1) * 100;
|
|
1400
1451
|
rows.push(`<div class="token-tooltip-ctx">Context <span class="token-point-ctx-bar"><span style="width:${width}%"></span></span> <span class="token-tooltip-ctx-val">${fmtCompact(point.ctx)}</span></div>`);
|
|
1401
1452
|
}
|
|
1402
1453
|
const u = point.usage;
|
|
@@ -1414,7 +1465,7 @@ function TokenStats({ point }) {
|
|
|
1414
1465
|
rows.push(`<div class="token-tooltip-total"><span>Accumulated tokens</span><span>${fmtCompact(point.total)}</span></div>`);
|
|
1415
1466
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "msg-tokens", "data-tippy-html": `<div class="token-tooltip">${rows.join("")}</div>`, children: fmtCompact(point.total) });
|
|
1416
1467
|
}
|
|
1417
|
-
function UserRow({ turns, point }) {
|
|
1468
|
+
function UserRow({ turns, point, ctxLimit }) {
|
|
1418
1469
|
const [open, setOpen] = useCollapsed(false);
|
|
1419
1470
|
const msg = turns.find((t) => !isContextTurn(t));
|
|
1420
1471
|
const ctxItems = turns.filter(isContextTurn).flatMap((t) => t.blocks);
|
|
@@ -1423,6 +1474,7 @@ function UserRow({ turns, point }) {
|
|
|
1423
1474
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "msg-header", children: [
|
|
1424
1475
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { className: "msg-icon" }),
|
|
1425
1476
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "msg-author", children: "You" }),
|
|
1477
|
+
msg.queued && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "msg-summary", children: "queued during the previous turn" }),
|
|
1426
1478
|
ctxItems.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "msg-summary", onClick: () => setOpen((v) => !v), children: [
|
|
1427
1479
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "aux-chevron", children: open ? "▾" : "▸" }),
|
|
1428
1480
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
@@ -1433,7 +1485,7 @@ function UserRow({ turns, point }) {
|
|
|
1433
1485
|
] })
|
|
1434
1486
|
] }),
|
|
1435
1487
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "msg-header-right", children: [
|
|
1436
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TokenStats, { point }),
|
|
1488
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TokenStats, { point, ctxLimit }),
|
|
1437
1489
|
msg.ts != null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "msg-time", children: format(msg.ts, "HH:mm:ss") })
|
|
1438
1490
|
] })
|
|
1439
1491
|
] }),
|
|
@@ -1441,7 +1493,7 @@ function UserRow({ turns, point }) {
|
|
|
1441
1493
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TurnRow, { turn: msg })
|
|
1442
1494
|
] });
|
|
1443
1495
|
}
|
|
1444
|
-
function AssistantCard({ turns, point, duration: duration2, showAuthor = true }) {
|
|
1496
|
+
function AssistantCard({ turns, point, ctxLimit, duration: duration2, showAuthor = true }) {
|
|
1445
1497
|
const [open, setOpen] = useCollapsed(false);
|
|
1446
1498
|
const toolBlocks = turns.flatMap((t) => t.blocks.filter((b) => b.type === "tool_use"));
|
|
1447
1499
|
const errorCount = toolBlocks.filter((b) => b.result?.is_error).length;
|
|
@@ -1475,7 +1527,7 @@ function AssistantCard({ turns, point, duration: duration2, showAuthor = true })
|
|
|
1475
1527
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: summary })
|
|
1476
1528
|
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "msg-summary", children: summary })),
|
|
1477
1529
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "msg-header-right", children: [
|
|
1478
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TokenStats, { point }),
|
|
1530
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TokenStats, { point, ctxLimit }),
|
|
1479
1531
|
end != null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "msg-time", title: duration2 > 0 ? `Response time: ${fmtDuration(duration2)}` : null, children: format(end, "HH:mm:ss") })
|
|
1480
1532
|
] })
|
|
1481
1533
|
] }),
|
|
@@ -1558,11 +1610,43 @@ const ATTACHMENT_RENDERERS = {
|
|
|
1558
1610
|
title: `${a.addedNames?.length || 0} deferred tools ${a.removedNames?.length ? `(removed: ${a.removedNames.length})` : ""}`,
|
|
1559
1611
|
body: safeJson({ added: a.addedNames, removed: a.removedNames }),
|
|
1560
1612
|
defaultOpen: false
|
|
1613
|
+
}),
|
|
1614
|
+
// The one nested shape the fallback below can't flatten: files → diagnostics → range.
|
|
1615
|
+
diagnostics: (a) => {
|
|
1616
|
+
const lines = (a.files || []).flatMap((f) => (f.diagnostics || []).map((d) => `${f.uri}:${(d.range?.start?.line ?? 0) + 1} ${d.severity}: ${d.message}${d.source ? ` (${d.source})` : ""}`));
|
|
1617
|
+
return { title: `Diagnostics: ${lines.length} in ${a.files?.length || 0} file${a.files?.length === 1 ? "" : "s"}`, body: lines.join("\n") };
|
|
1618
|
+
},
|
|
1619
|
+
// addedLines carry each agent's one-line description; addedTypes is just their names.
|
|
1620
|
+
agent_listing_delta: (a) => ({
|
|
1621
|
+
title: `${a.addedTypes?.length || 0} agent types${a.removedTypes?.length ? ` (removed: ${a.removedTypes.length})` : ""}`,
|
|
1622
|
+
body: (a.addedLines || a.addedTypes || []).join("\n"),
|
|
1623
|
+
defaultOpen: false
|
|
1561
1624
|
})
|
|
1562
1625
|
};
|
|
1626
|
+
function genericAttachment(att) {
|
|
1627
|
+
const short = [], long = [];
|
|
1628
|
+
const collect = (v, key) => {
|
|
1629
|
+
if (typeof v === "string") {
|
|
1630
|
+
if (v.trim()) (v.length > 100 ? long : short).push([key, v]);
|
|
1631
|
+
} else if (Array.isArray(v)) {
|
|
1632
|
+
for (const x of v) collect(x, key);
|
|
1633
|
+
} else if (v && typeof v === "object") {
|
|
1634
|
+
for (const [k, x] of Object.entries(v)) collect(x, k);
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
for (const [k, v] of Object.entries(att || {})) {
|
|
1638
|
+
if (k !== "type") collect(v, k);
|
|
1639
|
+
}
|
|
1640
|
+
const detail = short.find(([k]) => /path|file|name|dir|uri|url|date/i.test(k))?.[1];
|
|
1641
|
+
const label = (att?.type || "attachment").replace(/_/g, " ").replace(/^./, (c) => c.toUpperCase());
|
|
1642
|
+
return {
|
|
1643
|
+
title: detail ? `${label}: ${detail}` : label,
|
|
1644
|
+
body: long.length ? long.map(([, v]) => v).join("\n\n") : short.length > 1 ? safeJson(att) : null
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1563
1647
|
function Attachment({ att }) {
|
|
1564
1648
|
const render = ATTACHMENT_RENDERERS[att?.type];
|
|
1565
|
-
const { title, body, defaultOpen = false } = render ? render(att) :
|
|
1649
|
+
const { title, body, defaultOpen = false } = render ? render(att) : genericAttachment(att);
|
|
1566
1650
|
if (body == null) return /* @__PURE__ */ jsxRuntimeExports.jsx(Label, { title, className: "attachment" });
|
|
1567
1651
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(Collapsible, { title, className: "attachment", defaultOpen, children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { children: body }) });
|
|
1568
1652
|
}
|
|
@@ -1592,12 +1676,26 @@ function safeJson(x) {
|
|
|
1592
1676
|
function JsonBlock({ value }) {
|
|
1593
1677
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { className: "block-text", text: "```json\n" + safeJson(value) + "\n```" });
|
|
1594
1678
|
}
|
|
1595
|
-
function
|
|
1679
|
+
function InstructionRun({ turns, model }) {
|
|
1680
|
+
const tokens2 = turns.map((t) => countTokens(t.blocks[0].it.content, t.blocks[0].it.model ?? model));
|
|
1681
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
1682
|
+
turns.map((t, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(InstructionFile, { it: t.blocks[0].it, model, tokens: tokens2[i] }, t.uuid)),
|
|
1683
|
+
turns.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "aux instruction-total", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "instruction-tokens", children: [
|
|
1684
|
+
"~ ",
|
|
1685
|
+
fmtCompact(tokens2.reduce((a, b) => a + b, 0))
|
|
1686
|
+
] }) })
|
|
1687
|
+
] });
|
|
1688
|
+
}
|
|
1689
|
+
function InstructionFile({ it, model, tokens: tokens2 }) {
|
|
1596
1690
|
const [open, setOpen] = useCollapsed(false);
|
|
1597
1691
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "aux instruction-file", children: [
|
|
1598
1692
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "aux-toggle", onClick: () => setOpen((v) => !v), children: [
|
|
1599
1693
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "aux-chevron", children: open ? "▾" : "▸" }),
|
|
1600
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: instructionTitle(it, model) })
|
|
1694
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: instructionTitle(it, model) }),
|
|
1695
|
+
tokens2 > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "instruction-tokens", title: "Approx. tokens", children: [
|
|
1696
|
+
"~ ",
|
|
1697
|
+
fmtCompact(tokens2)
|
|
1698
|
+
] })
|
|
1601
1699
|
] }),
|
|
1602
1700
|
open && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "aux-body", children: [
|
|
1603
1701
|
it.name && it.name !== it.file_path && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "instruction-path", children: it.file_path }),
|
|
@@ -1831,7 +1929,6 @@ function RequestsView({ sessionId, date, granularity = "day", fileSize = 0, expa
|
|
|
1831
1929
|
] })
|
|
1832
1930
|
] });
|
|
1833
1931
|
}
|
|
1834
|
-
const CONTEXT_WINDOW$1 = THRESHOLDS.context.danger;
|
|
1835
1932
|
let MAX_LINES = 10;
|
|
1836
1933
|
let MAX_LINE_CHARS = 200;
|
|
1837
1934
|
let MODEL = null;
|
|
@@ -1880,7 +1977,7 @@ function renderTurn(t) {
|
|
|
1880
1977
|
if (t.role === "instruction") return `**Instructions loaded:**
|
|
1881
1978
|
|
|
1882
1979
|
${t.blocks.map(renderBlock).join("\n\n")}`;
|
|
1883
|
-
const role = t.role === "user" ? "User" : `Assistant`;
|
|
1980
|
+
const role = t.role === "user" ? t.queued ? "User (queued)" : "User" : `Assistant`;
|
|
1884
1981
|
const tokens2 = t.tokenTotal > 0 && t.tokenDelta != null ? ` (${t.tokenDelta >= 0 ? "+" : ""}${fmtCompact(t.tokenDelta)} / ${fmtCompact(t.tokenTotal)} tokens)` : "";
|
|
1885
1982
|
return `**${role}${tokens2}:**
|
|
1886
1983
|
|
|
@@ -1894,7 +1991,8 @@ function markdownSession(meta, items, truncated, instructions = []) {
|
|
|
1894
1991
|
const t = meta.tokens;
|
|
1895
1992
|
const stu = meta.serverToolUse;
|
|
1896
1993
|
const wallDuration = meta.lastActivityAt - meta.startedAt;
|
|
1897
|
-
const
|
|
1994
|
+
const ctxLimit = contextWindow(meta.models);
|
|
1995
|
+
const contextPct = Math.round(meta.lastContextTokens / ctxLimit * 100);
|
|
1898
1996
|
const cacheHitPct = (meta.cacheHitRatio * 100).toFixed(0);
|
|
1899
1997
|
const turns = items ? flatten(items, instructions) : null;
|
|
1900
1998
|
MODEL = turns ? currentModel(turns) : null;
|
|
@@ -1910,7 +2008,7 @@ ${transcript}`;
|
|
|
1910
2008
|
- Estimated cost: ${fmtUSD(meta.cost)}
|
|
1911
2009
|
- Total tokens: ${fmtCompact(meta.totalTokens)}
|
|
1912
2010
|
- Working time: ${fmtDuration(meta.activeMs)}
|
|
1913
|
-
- Context size: ${fmtCompact(meta.lastContextTokens)} / ${fmtCompact(
|
|
2011
|
+
- Context size: ${fmtCompact(meta.lastContextTokens)} / ${fmtCompact(ctxLimit)} (${contextPct}%)${meta.savings.using5mCache > 0 ? `
|
|
1914
2012
|
|
|
1915
2013
|
## Potential savings
|
|
1916
2014
|
- with 5m cache: ${meta.cost > 0 ? `(${(meta.savings.using5mCache / meta.cost * 100).toFixed(0)}%) ` : ""}−${fmtUSD(meta.savings.using5mCache)}` : ""}
|
|
@@ -2078,7 +2176,6 @@ ${body}`;
|
|
|
2078
2176
|
] })
|
|
2079
2177
|
] });
|
|
2080
2178
|
}
|
|
2081
|
-
const CONTEXT_WINDOW = THRESHOLDS.context.danger;
|
|
2082
2179
|
function SessionSummary({ meta, items, instructions, agent, onOpenAgent, granularity = "day" }) {
|
|
2083
2180
|
const timeFormat = granularity === "day" ? "pp" : "MMM d, pp";
|
|
2084
2181
|
const onAnalyze = () => {
|
|
@@ -2097,7 +2194,8 @@ ${body}`);
|
|
|
2097
2194
|
const stu = meta.serverToolUse;
|
|
2098
2195
|
const hasServerTools = stu.webSearch > 0 || stu.webFetch > 0;
|
|
2099
2196
|
const modelLabel = meta.models.join(", ") || "—";
|
|
2100
|
-
const
|
|
2197
|
+
const ctxLimit = contextWindow(meta.models);
|
|
2198
|
+
const ctxTone = tone(meta.lastContextTokens, THRESHOLDS.context);
|
|
2101
2199
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "session-summary", children: [
|
|
2102
2200
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "summary-ai", children: [
|
|
2103
2201
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "summary-ai-row", children: [
|
|
@@ -2132,7 +2230,7 @@ ${body}`);
|
|
|
2132
2230
|
] }) }),
|
|
2133
2231
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Total tokens", value: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: tone(totalTokens2, THRESHOLDS.tokens), children: fmtCompact(totalTokens2) }) }),
|
|
2134
2232
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Working time", value: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: tone(activeDuration, THRESHOLDS.workTime), children: fmtDuration(activeDuration) }) }),
|
|
2135
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Context size", value: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className:
|
|
2233
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Context size", value: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: ctxTone, children: `${fmtCompact(meta.lastContextTokens)} / ${fmtCompact(ctxLimit)}` }), below: /* @__PURE__ */ jsxRuntimeExports.jsx(Bar, { ratio: meta.lastContextTokens / ctxLimit, tone: ctxTone }) })
|
|
2136
2234
|
] }),
|
|
2137
2235
|
meta.savings.using5mCache > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(Section, { title: "Potential savings", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2138
2236
|
Field,
|
|
@@ -2261,7 +2359,7 @@ const TABS = [
|
|
|
2261
2359
|
{ key: "jsonl", label: "Raw Logs" },
|
|
2262
2360
|
{ key: "requests", label: "API Requests" }
|
|
2263
2361
|
];
|
|
2264
|
-
function Session({ meta, date, granularity = "day" }) {
|
|
2362
|
+
function Session({ meta, missing, date, granularity = "day" }) {
|
|
2265
2363
|
const [items, setItems] = reactExports.useState(null);
|
|
2266
2364
|
const [instructions, setInstructions] = reactExports.useState([]);
|
|
2267
2365
|
const [mode, setMode] = useLocalStorage("session.view-mode", "conversation");
|
|
@@ -2287,7 +2385,10 @@ function Session({ meta, date, granularity = "day" }) {
|
|
|
2287
2385
|
};
|
|
2288
2386
|
}, [sessionId, date, granularity, fileSize]);
|
|
2289
2387
|
if (!meta) {
|
|
2290
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "session-view empty", children: /* @__PURE__ */ jsxRuntimeExports.
|
|
2388
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "session-view empty", children: missing?.warn ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "session-missing", children: [
|
|
2389
|
+
"⚠ ",
|
|
2390
|
+
missing.text
|
|
2391
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: missing?.text || "Select a session to inspect." }) });
|
|
2291
2392
|
}
|
|
2292
2393
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "session-view", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "view-body", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "view-conversation", children: agentOpen ? /* @__PURE__ */ jsxRuntimeExports.jsx(AgentView, { meta, items, instructions, agent, onClose: () => setAgentOpen(false) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "view-tab-pane", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "view-tab-pane-row", children: [
|
|
2293
2394
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "view-tab-pane-main", children: [
|
|
@@ -2471,6 +2572,7 @@ function App() {
|
|
|
2471
2572
|
const [granularity, setGranularity] = useLocalStorage("gantt.granularity", "day");
|
|
2472
2573
|
const [anchor, setAnchor] = reactExports.useState(() => startOfPeriod(Date.now(), granularity));
|
|
2473
2574
|
const [selectedId, setSelectedId] = reactExports.useState(null);
|
|
2575
|
+
const [deepLink, setDeepLink] = reactExports.useState(null);
|
|
2474
2576
|
const [sourceFilter, setSourceFilter] = reactExports.useState(null);
|
|
2475
2577
|
const [projectFilter, setProjectFilter] = reactExports.useState(null);
|
|
2476
2578
|
const { defaultLayout, onLayoutChanged } = sn({ id: "app.body", panelIds: ["list", "detail"], storage: localStorage });
|
|
@@ -2493,7 +2595,7 @@ function App() {
|
|
|
2493
2595
|
setLoading(false);
|
|
2494
2596
|
}
|
|
2495
2597
|
});
|
|
2496
|
-
}, 120);
|
|
2598
|
+
}, loading ? 0 : 120);
|
|
2497
2599
|
const off = window.api.onSessionsUpdate((s) => {
|
|
2498
2600
|
if (!cancelled) setSessions(s || []);
|
|
2499
2601
|
});
|
|
@@ -2514,10 +2616,31 @@ function App() {
|
|
|
2514
2616
|
() => sessions.find((s) => s.sessionId === selectedId) || null,
|
|
2515
2617
|
[sessions, selectedId]
|
|
2516
2618
|
);
|
|
2517
|
-
|
|
2619
|
+
let missing = null;
|
|
2620
|
+
if (!selected && scanProgress?.scanning === false) {
|
|
2621
|
+
const when = granularity === "day" ? `on ${format(anchor, "MMM d, yyyy")}` : `in this ${granularity}`;
|
|
2622
|
+
if (!sessions.length) missing = { text: `No sessions recorded ${when}.` };
|
|
2623
|
+
else if (deepLink?.date === format(anchor, "yyyy-MM-dd")) missing = { text: `Session ${selectedId} has no records ${when}.`, warn: true };
|
|
2624
|
+
}
|
|
2625
|
+
const selectSession = reactExports.useCallback((id, link = null) => {
|
|
2518
2626
|
closeFind();
|
|
2519
2627
|
setSelectedId(id);
|
|
2628
|
+
setDeepLink(link);
|
|
2520
2629
|
}, []);
|
|
2630
|
+
reactExports.useEffect(() => {
|
|
2631
|
+
const open = ({ id, date }) => {
|
|
2632
|
+
const day = date && parse(date, "yyyy-MM-dd", /* @__PURE__ */ new Date());
|
|
2633
|
+
if (day && !isNaN(day)) {
|
|
2634
|
+
setGranularity("day");
|
|
2635
|
+
setAnchor(startOfPeriod(day, "day"));
|
|
2636
|
+
setSourceFilter(null);
|
|
2637
|
+
setProjectFilter(null);
|
|
2638
|
+
}
|
|
2639
|
+
selectSession(id, { id, date });
|
|
2640
|
+
};
|
|
2641
|
+
window.api.takeDeepLink().then((target) => target && open(target));
|
|
2642
|
+
return window.api.onDeepLink(open);
|
|
2643
|
+
}, [selectSession, setGranularity]);
|
|
2521
2644
|
const shiftPeriod = reactExports.useCallback((delta) => {
|
|
2522
2645
|
setProjectFilter(null);
|
|
2523
2646
|
setAnchor((a) => startOfPeriod(addPeriod(a, granularity, delta), granularity));
|
|
@@ -2587,11 +2710,12 @@ function App() {
|
|
|
2587
2710
|
{
|
|
2588
2711
|
sessions: dayItems.past,
|
|
2589
2712
|
selectedId,
|
|
2713
|
+
deepLink,
|
|
2590
2714
|
onSelect: selectSession
|
|
2591
2715
|
}
|
|
2592
2716
|
) }),
|
|
2593
2717
|
/* @__PURE__ */ jsxRuntimeExports.jsx(tn, { className: "resize-handle resize-handle-v" }),
|
|
2594
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Zt, { id: "detail", minSize: 30, className: "body-pane", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Session, { meta: selected, date: format(anchor, "yyyy-MM-dd"), granularity }) })
|
|
2718
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Zt, { id: "detail", minSize: 30, className: "body-pane", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Session, { meta: selected, missing, date: format(anchor, "yyyy-MM-dd"), granularity }) })
|
|
2595
2719
|
]
|
|
2596
2720
|
}
|
|
2597
2721
|
) })
|