@wrongstack/webui 0.273.0 → 0.273.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/wrongstack.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>WrongStack WebUI</title>
8
- <script type="module" crossorigin src="/assets/index-CM62rXoC.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-D0dNaLPf.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-QTnfLwEv.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-P9eRrO6V.js">
11
11
  <link rel="stylesheet" crossorigin href="/assets/vendor-Doh9e_v3.css">
package/dist/index.js CHANGED
@@ -185,7 +185,7 @@ function httpOriginForAuth() {
185
185
  }
186
186
 
187
187
  // src/lib/ws-client.ts
188
- var WrongStackWebSocketClient = class {
188
+ var WrongStackWebSocketClient = class _WrongStackWebSocketClient {
189
189
  ws = null;
190
190
  url;
191
191
  handlers = /* @__PURE__ */ new Map();
@@ -195,6 +195,14 @@ var WrongStackWebSocketClient = class {
195
195
  shouldReconnect = true;
196
196
  reconnectTimer = null;
197
197
  messageQueue = [];
198
+ // Cap on the offline-queue depth. Past this, send() drops the OLDEST
199
+ // queued message before appending the new one (FIFO drop). Bounds
200
+ // memory under long disconnects and prevents stale commands from
201
+ // flooding the server on reconnect -- a 50k-deep queue of stale
202
+ // user_messages re-firing on next open would just confuse the model
203
+ // and waste its context window. 1000 is a generous budget for a
204
+ // genuine reconnect window (typical reconnect <10s; <50 msg/s).
205
+ static MAX_QUEUED_MESSAGES = 1e3;
198
206
  pendingConfirms = /* @__PURE__ */ new Map();
199
207
  sessionId = null;
200
208
  /** Stored last close reason / error message so the UI can show "what
@@ -440,6 +448,18 @@ var WrongStackWebSocketClient = class {
440
448
  if (this.ws?.readyState === WebSocket.OPEN) {
441
449
  this.ws.send(JSON.stringify(message));
442
450
  } else {
451
+ if (this.messageQueue.length >= _WrongStackWebSocketClient.MAX_QUEUED_MESSAGES) {
452
+ const dropped = this.messageQueue.shift();
453
+ if (dropped) {
454
+ console.warn(JSON.stringify({
455
+ level: "warn",
456
+ event: "ws_client.message_queue_full",
457
+ cap: _WrongStackWebSocketClient.MAX_QUEUED_MESSAGES,
458
+ droppedType: dropped.type,
459
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
460
+ }));
461
+ }
462
+ }
443
463
  this.messageQueue.push(message);
444
464
  }
445
465
  }
@@ -742,6 +762,7 @@ var WrongStackWebSocketClient = class {
742
762
  clearTimeout(this.reconnectTimer);
743
763
  this.reconnectTimer = null;
744
764
  }
765
+ this.messageQueue.length = 0;
745
766
  this.ws?.close();
746
767
  this.ws = null;
747
768
  }
@@ -1536,6 +1557,10 @@ function pushTimeline(timeline, event) {
1536
1557
  function bumpSparkline(bins) {
1537
1558
  return [bins[0] + 1, ...bins.slice(0, SPARKLINE_BINS - 1)];
1538
1559
  }
1560
+ function clampContextPct(pct) {
1561
+ if (!Number.isFinite(pct)) return 0;
1562
+ return Math.max(0, Math.min(100, pct));
1563
+ }
1539
1564
  var useFleetStore = create10()((set, get) => ({
1540
1565
  agents: /* @__PURE__ */ new Map(),
1541
1566
  leaderId: void 0,
@@ -1703,7 +1728,7 @@ var useFleetStore = create10()((set, get) => ({
1703
1728
  });
1704
1729
  break;
1705
1730
  case "ctx_pct":
1706
- next.ctxPct = Math.round(Math.max(0, e.load ?? 0) * 100);
1731
+ next.ctxPct = clampContextPct(Math.round(Math.max(0, e.load ?? 0) * 100));
1707
1732
  next.ctxTokens = e.tokens ?? next.ctxTokens;
1708
1733
  next.maxContext = e.maxContext ?? next.maxContext;
1709
1734
  if (next.ctxPct >= 80 && !next.budgetWarning) {
@@ -2033,6 +2058,10 @@ var useFileStore = create17()((set, get) => ({
2033
2058
 
2034
2059
  // src/stores/viz-store.ts
2035
2060
  import { create as create18 } from "zustand";
2061
+ function contextPctFromLoad(load) {
2062
+ const value = typeof load === "number" && Number.isFinite(load) ? load : 0;
2063
+ return Math.max(0, Math.min(100, Math.round(value * 100)));
2064
+ }
2036
2065
  function inferKind(event, isTarget = false) {
2037
2066
  if (isTarget && event.target) {
2038
2067
  if (event.kind === "agent:tool") return "tool";
@@ -2472,7 +2501,7 @@ function wsToVizEvent(wsType, payload) {
2472
2501
  timestamp: Date.now(),
2473
2502
  source: agentId,
2474
2503
  target: "session",
2475
- label: `ctx ${Math.round((payload.load ?? 0) * 100)}%`,
2504
+ label: `ctx ${contextPctFromLoad(payload.load)}%`,
2476
2505
  magnitude: payload.tokens ?? 0,
2477
2506
  data: payload,
2478
2507
  color: NODE_COLORS.agent,
@@ -2570,7 +2599,6 @@ function wsToVizEvent(wsType, payload) {
2570
2599
  };
2571
2600
  }
2572
2601
  case "ctx.pct": {
2573
- const load = payload.load ?? 0;
2574
2602
  const tokens = payload.tokens ?? 0;
2575
2603
  return {
2576
2604
  id: nextId(),
@@ -2578,7 +2606,7 @@ function wsToVizEvent(wsType, payload) {
2578
2606
  timestamp: Date.now(),
2579
2607
  source: "leader",
2580
2608
  target: "session",
2581
- label: `ctx ${Math.round(load * 100)}%`,
2609
+ label: `ctx ${contextPctFromLoad(payload.load)}%`,
2582
2610
  magnitude: tokens,
2583
2611
  data: payload,
2584
2612
  color: NODE_COLORS.agent,
@@ -2710,6 +2738,20 @@ function deriveMonitorStats(sessions) {
2710
2738
  }
2711
2739
  return { clientCounts, totalAgents, activeAgents, aggregate };
2712
2740
  }
2741
+ function clampCtxPct(pct) {
2742
+ if (pct === void 0) return void 0;
2743
+ if (!Number.isFinite(pct)) return 0;
2744
+ return Math.max(0, Math.min(100, Math.round(pct)));
2745
+ }
2746
+ function normalizeLiveSessions(sessions) {
2747
+ return sessions.map((session) => ({
2748
+ ...session,
2749
+ agents: session.agents.map((agent) => ({
2750
+ ...agent,
2751
+ ctxPct: clampCtxPct(agent.ctxPct)
2752
+ }))
2753
+ }));
2754
+ }
2713
2755
  var mailActivitySeq = 0;
2714
2756
  var useMonitorStore = create19()((set) => ({
2715
2757
  clientCounts: { tui: 0, webui: 0, repl: 0 },
@@ -2724,11 +2766,14 @@ var useMonitorStore = create19()((set) => ({
2724
2766
  aggregate: { toolCalls: 0, costUsd: 0, tokensIn: 0, tokensOut: 0 },
2725
2767
  lastUpdated: Date.now(),
2726
2768
  setClientCounts: (counts) => set({ clientCounts: counts, lastUpdated: Date.now() }),
2727
- setLiveSessions: (sessions) => set({
2728
- liveSessions: sessions,
2729
- ...deriveMonitorStats(sessions),
2730
- lastUpdated: Date.now()
2731
- }),
2769
+ setLiveSessions: (sessions) => {
2770
+ const normalized = normalizeLiveSessions(sessions);
2771
+ set({
2772
+ liveSessions: normalized,
2773
+ ...deriveMonitorStats(normalized),
2774
+ lastUpdated: Date.now()
2775
+ });
2776
+ },
2732
2777
  addMailActivity: (activity) => set((state) => ({
2733
2778
  mailActivity: [{ ...activity, seq: activity.seq ?? ++mailActivitySeq }, ...state.mailActivity].slice(0, 50),
2734
2779
  lastUpdated: Date.now()
@@ -3661,10 +3706,18 @@ function handleContextCompacted(msg) {
3661
3706
  function handleCompactionFailed(msg) {
3662
3707
  pipeViz2(msg);
3663
3708
  const payload = msg.payload;
3664
- const load = payload.maxContext > 0 ? Math.round(payload.tokens / payload.maxContext * 100) : 0;
3709
+ let load;
3710
+ let label;
3711
+ if (payload.budget && payload.budget.availableInputTokens > 0) {
3712
+ load = Math.min(100, Math.max(0, Math.round(payload.budget.load * 100)));
3713
+ label = "input budget";
3714
+ } else {
3715
+ load = payload.maxContext > 0 ? Math.min(100, Math.max(0, Math.round(payload.tokens / payload.maxContext * 100))) : 0;
3716
+ label = "context";
3717
+ }
3665
3718
  useChatStore.getState().addMessage({
3666
3719
  role: "assistant",
3667
- content: `Compaction failed at ${payload.level} (${load}% context): ${payload.message}`,
3720
+ content: `Compaction failed at ${payload.level} (${load}% ${label}): ${payload.message}`,
3668
3721
  isError: payload.fatal
3669
3722
  });
3670
3723
  toast.error(`Compaction failed: ${payload.message}`);
@@ -11545,6 +11598,10 @@ function getTextColor(pct) {
11545
11598
  if (pct >= 60) return "text-[hsl(var(--warning))]";
11546
11599
  return "text-[hsl(var(--success))]";
11547
11600
  }
11601
+ function clampPct(pct) {
11602
+ if (!Number.isFinite(pct)) return 0;
11603
+ return Math.max(0, Math.min(100, pct));
11604
+ }
11548
11605
  function ContextBar({
11549
11606
  pct,
11550
11607
  tokens,
@@ -11554,7 +11611,7 @@ function ContextBar({
11554
11611
  className,
11555
11612
  onClick
11556
11613
  }) {
11557
- const clamped = Math.max(0, Math.min(200, pct));
11614
+ const clamped = clampPct(pct);
11558
11615
  const eighths = Math.round(clamped / 100 * segments * 8);
11559
11616
  const bars = [];
11560
11617
  let remaining = eighths;
@@ -11563,7 +11620,7 @@ function ContextBar({
11563
11620
  bars.push({ fill: segFill });
11564
11621
  remaining -= segFill;
11565
11622
  }
11566
- const pctText = pct >= 100 ? `${Math.round(pct)}%+` : `${Math.round(pct)}%`;
11623
+ const pctText = `${Math.round(clamped)}%`;
11567
11624
  const tokenText = showTokens && tokens !== void 0 && maxTokens !== void 0 && maxTokens > 0 ? ` ${fmtTok(tokens)}/${fmtTok(maxTokens)}` : "";
11568
11625
  return /* @__PURE__ */ jsxs28(
11569
11626
  "span",
@@ -11571,8 +11628,8 @@ function ContextBar({
11571
11628
  className: cn(
11572
11629
  "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-mono font-medium shrink-0",
11573
11630
  onClick && "cursor-pointer hover:ring-1 hover:ring-ring transition-shadow",
11574
- getTextColor(pct),
11575
- pct >= 75 ? "bg-red-500/10" : pct >= 60 ? "bg-amber-500/10" : "bg-emerald-500/10",
11631
+ getTextColor(clamped),
11632
+ clamped >= 75 ? "bg-red-500/10" : clamped >= 60 ? "bg-amber-500/10" : "bg-emerald-500/10",
11576
11633
  className
11577
11634
  ),
11578
11635
  title: (tokens !== void 0 && maxTokens !== void 0 ? `Context window: ${tokens.toLocaleString()} / ${maxTokens.toLocaleString()} tokens (${pctText})` : `Context window: ${pctText}`) + (onClick ? " \u2014 click for breakdown" : ""),
@@ -11595,7 +11652,7 @@ function ContextBar({
11595
11652
  {
11596
11653
  className: cn(
11597
11654
  "tabular-nums w-[0.55em] text-center",
11598
- b.fill > 0 ? getTextColor(pct) : "text-muted-foreground/30"
11655
+ b.fill > 0 ? getTextColor(clamped) : "text-muted-foreground/30"
11599
11656
  ),
11600
11657
  children: SEGMENT_FILL[b.fill] ?? " "
11601
11658
  },
@@ -11615,8 +11672,8 @@ function ContextFillBar({
11615
11672
  className,
11616
11673
  onClick
11617
11674
  }) {
11618
- const clamped = Math.max(0, Math.min(200, pct));
11619
- const pctText = pct > 100 ? `${Math.round(pct)}%+` : `${Math.round(pct)}%`;
11675
+ const clamped = clampPct(pct);
11676
+ const pctText = `${Math.round(clamped)}%`;
11620
11677
  const tokenText = showTokens && tokens !== void 0 && maxTokens !== void 0 && maxTokens > 0 ? ` ${fmtTok(tokens)}/${fmtTok(maxTokens)}` : "";
11621
11678
  return /* @__PURE__ */ jsxs28(
11622
11679
  "span",
@@ -11644,14 +11701,14 @@ function ContextFillBar({
11644
11701
  /* @__PURE__ */ jsx31("span", { className: "h-1.5 w-16 overflow-hidden rounded-full bg-muted shrink-0", children: /* @__PURE__ */ jsx31(
11645
11702
  "span",
11646
11703
  {
11647
- className: cn("h-full rounded-full transition-all duration-300", getColor(pct)),
11704
+ className: cn("h-full rounded-full transition-all duration-300", getColor(clamped)),
11648
11705
  style: { width: `${Math.max(2, clamped)}%` }
11649
11706
  }
11650
11707
  ) }),
11651
11708
  /* @__PURE__ */ jsx31(
11652
11709
  "span",
11653
11710
  {
11654
- className: cn("text-[11px] font-mono tabular-nums font-medium", getTextColor(pct)),
11711
+ className: cn("text-[11px] font-mono tabular-nums font-medium", getTextColor(clamped)),
11655
11712
  children: pctText
11656
11713
  }
11657
11714
  ),
@@ -14156,7 +14213,7 @@ function ChatView() {
14156
14213
  document.addEventListener("open:context-breakdown", handler);
14157
14214
  return () => document.removeEventListener("open:context-breakdown", handler);
14158
14215
  }, []);
14159
- const ctxPct = maxContext > 0 && lastInputTokens > 0 ? Math.round(lastInputTokens / maxContext * 100) : 0;
14216
+ const ctxPct = maxContext > 0 && lastInputTokens > 0 ? Math.min(100, Math.round(lastInputTokens / maxContext * 100)) : 0;
14160
14217
  const _ctxTone = ctxPct >= 85 ? "bg-red-500/15 text-red-600 dark:text-red-400" : ctxPct >= 70 ? "bg-amber-500/15 text-amber-600 dark:text-amber-400" : "bg-muted text-muted-foreground";
14161
14218
  const [pinnedToBottom, setPinnedToBottom] = useState33(true);
14162
14219
  const [unreadCount, setUnreadCount] = useState33(0);
@@ -20874,6 +20931,7 @@ function AgentDetail({
20874
20931
  const [copied, setCopied] = useState47(false);
20875
20932
  const leaderId = useFleetStore((s) => s.leaderId);
20876
20933
  const isLeader = agent.id === leaderId;
20934
+ const ctxPct = Math.min(100, Math.max(0, agent.ctxPct));
20877
20935
  const handleCopy = useCallback24(async (text) => {
20878
20936
  try {
20879
20937
  await navigator.clipboard.writeText(text);
@@ -20882,7 +20940,7 @@ function AgentDetail({
20882
20940
  } catch {
20883
20941
  }
20884
20942
  }, []);
20885
- const ctxTone = agent.ctxPct >= 85 ? "bg-red-500/15 text-red-600 dark:text-red-400" : agent.ctxPct >= 70 ? "bg-amber-500/15 text-amber-600 dark:text-amber-400" : "bg-muted text-muted-foreground";
20943
+ const ctxTone = ctxPct >= 85 ? "bg-red-500/15 text-red-600 dark:text-red-400" : ctxPct >= 70 ? "bg-amber-500/15 text-amber-600 dark:text-amber-400" : "bg-muted text-muted-foreground";
20886
20944
  return /* @__PURE__ */ jsx76("div", { className: "fixed inset-0 z-50 flex items-start justify-center pt-[10vh] bg-black/40 backdrop-blur-sm", children: /* @__PURE__ */ jsxs69("div", { className: "w-full max-w-xl max-h-[80vh] overflow-y-auto rounded-xl border bg-card shadow-2xl", children: [
20887
20945
  /* @__PURE__ */ jsxs69(
20888
20946
  "div",
@@ -20972,7 +21030,7 @@ function AgentDetail({
20972
21030
  /* @__PURE__ */ jsxs69("div", { className: "flex items-center justify-between text-[10px]", children: [
20973
21031
  /* @__PURE__ */ jsx76("span", { className: "text-muted-foreground", children: "Context window" }),
20974
21032
  /* @__PURE__ */ jsxs69("span", { className: cn("tabular font-medium", ctxTone.replace(/bg-\S+\s*/g, "")), children: [
20975
- agent.ctxPct,
21033
+ ctxPct,
20976
21034
  "%"
20977
21035
  ] })
20978
21036
  ] }),
@@ -20981,9 +21039,9 @@ function AgentDetail({
20981
21039
  {
20982
21040
  className: cn(
20983
21041
  "h-full rounded-full transition-all",
20984
- agent.ctxPct >= 85 ? "bg-destructive" : agent.ctxPct >= 70 ? "bg-[hsl(var(--warning))]" : "bg-primary"
21042
+ ctxPct >= 85 ? "bg-destructive" : ctxPct >= 70 ? "bg-[hsl(var(--warning))]" : "bg-primary"
20985
21043
  ),
20986
- style: { width: `${Math.min(200, Math.max(2, agent.ctxPct))}%` }
21044
+ style: { width: `${Math.max(2, ctxPct)}%` }
20987
21045
  }
20988
21046
  ) }),
20989
21047
  /* @__PURE__ */ jsxs69("div", { className: "text-[10px] text-muted-foreground tabular text-right", children: [
@@ -21094,6 +21152,7 @@ function AgentCard({
21094
21152
  const meta2 = STATUS_META2[a.status];
21095
21153
  const active = a.status === "running";
21096
21154
  const tool = a.currentTool ?? a.lastTool;
21155
+ const ctxPct = Math.min(100, Math.max(0, a.ctxPct));
21097
21156
  return /* @__PURE__ */ jsxs69(
21098
21157
  "button",
21099
21158
  {
@@ -21143,18 +21202,18 @@ function AgentCard({
21143
21202
  ]
21144
21203
  }
21145
21204
  ),
21146
- active && a.maxContext > 0 && /* @__PURE__ */ jsxs69("div", { className: "flex items-center gap-1", title: `Context ${a.ctxPct}%`, children: [
21205
+ active && a.maxContext > 0 && /* @__PURE__ */ jsxs69("div", { className: "flex items-center gap-1", title: `Context ${ctxPct}%`, children: [
21147
21206
  /* @__PURE__ */ jsx76("div", { className: "h-1 flex-1 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx76(
21148
21207
  "div",
21149
21208
  {
21150
21209
  className: cn(
21151
21210
  "h-full rounded-full transition-all",
21152
- a.ctxPct >= 85 ? "bg-destructive" : a.ctxPct >= 70 ? "bg-[hsl(var(--warning))]" : "bg-primary"
21211
+ ctxPct >= 85 ? "bg-destructive" : ctxPct >= 70 ? "bg-[hsl(var(--warning))]" : "bg-primary"
21153
21212
  ),
21154
- style: { width: `${Math.min(200, Math.max(2, a.ctxPct))}%` }
21213
+ style: { width: `${Math.max(2, ctxPct)}%` }
21155
21214
  }
21156
21215
  ) }),
21157
- /* @__PURE__ */ jsx76("span", { className: "tabular text-[9px] text-muted-foreground", children: a.maxContext > 0 ? `${a.ctxPct}%` : "\u2014" })
21216
+ /* @__PURE__ */ jsx76("span", { className: "tabular text-[9px] text-muted-foreground", children: a.maxContext > 0 ? `${ctxPct}%` : "\u2014" })
21158
21217
  ] }),
21159
21218
  a.error && /* @__PURE__ */ jsx76(
21160
21219
  "div",
@@ -21920,7 +21979,7 @@ function SessionPanel() {
21920
21979
  () => Array.from(fleetAgents.values()).filter((a) => a.status === "running").length,
21921
21980
  [fleetAgents]
21922
21981
  );
21923
- const ctxPct = maxContext > 0 && lastInputTokens > 0 ? Math.round(lastInputTokens / maxContext * 100) : 0;
21982
+ const ctxPct = maxContext > 0 && lastInputTokens > 0 ? Math.min(100, Math.round(lastInputTokens / maxContext * 100)) : 0;
21924
21983
  const pinnedRows = pinnedIds.map((id) => messages.find((m) => m.id === id)).filter((m) => !!m && m.content.length > 0);
21925
21984
  const send = (msg) => getWSClient(wsUrl)?.send?.(msg);
21926
21985
  return /* @__PURE__ */ jsxs74("div", { className: "flex-1 overflow-y-auto", children: [
@@ -25037,6 +25096,7 @@ function FleetAgentDetailPanel({
25037
25096
  const [showFullToolLog, setShowFullToolLog] = useState60(false);
25038
25097
  const meta2 = STATUS_META6[agent.status];
25039
25098
  const active = agent.status === "running";
25099
+ const ctxPct = Math.min(100, Math.max(0, agent.ctxPct));
25040
25100
  const handleCopy = useCallback35(async (text) => {
25041
25101
  try {
25042
25102
  await navigator.clipboard.writeText(text);
@@ -25155,13 +25215,13 @@ function FleetAgentDetailPanel({
25155
25215
  {
25156
25216
  className: cn(
25157
25217
  "h-full rounded-full transition-all",
25158
- agent.ctxPct >= 85 ? "bg-destructive" : agent.ctxPct >= 70 ? "bg-amber-500" : "bg-emerald-500"
25218
+ ctxPct >= 85 ? "bg-destructive" : ctxPct >= 70 ? "bg-amber-500" : "bg-emerald-500"
25159
25219
  ),
25160
- style: { width: `${Math.min(200, agent.ctxPct)}%` }
25220
+ style: { width: `${ctxPct}%` }
25161
25221
  }
25162
25222
  ) }),
25163
25223
  /* @__PURE__ */ jsxs89("span", { className: "text-[10px] text-muted-foreground tabular-nums font-mono", children: [
25164
- agent.ctxPct,
25224
+ ctxPct,
25165
25225
  "% used"
25166
25226
  ] })
25167
25227
  ] })
@@ -25295,6 +25355,7 @@ function FleetAgentRow({
25295
25355
  }) {
25296
25356
  const meta2 = STATUS_META6[agent.status];
25297
25357
  const active = agent.status === "running";
25358
+ const ctxPct = Math.min(100, Math.max(0, agent.ctxPct));
25298
25359
  return /* @__PURE__ */ jsxs89(
25299
25360
  "button",
25300
25361
  {
@@ -25331,12 +25392,12 @@ function FleetAgentRow({
25331
25392
  {
25332
25393
  className: cn(
25333
25394
  "h-full rounded-full transition-all",
25334
- agent.ctxPct >= 85 ? "bg-destructive" : agent.ctxPct >= 70 ? "bg-amber-500" : "bg-emerald-500"
25395
+ ctxPct >= 85 ? "bg-destructive" : ctxPct >= 70 ? "bg-amber-500" : "bg-emerald-500"
25335
25396
  ),
25336
- style: { width: `${Math.min(200, agent.ctxPct)}%` }
25397
+ style: { width: `${ctxPct}%` }
25337
25398
  }
25338
25399
  ) }),
25339
- /* @__PURE__ */ jsx96("span", { className: "text-[9px] tabular-nums text-muted-foreground font-mono leading-none", children: agent.maxContext > 0 ? `${agent.ctxPct}%` : "\u2014" })
25400
+ /* @__PURE__ */ jsx96("span", { className: "text-[9px] tabular-nums text-muted-foreground font-mono leading-none", children: agent.maxContext > 0 ? `${ctxPct}%` : "\u2014" })
25340
25401
  ] }),
25341
25402
  /* @__PURE__ */ jsx96("span", { className: "tabular-nums text-[10px] text-muted-foreground", children: agent.extensions > 0 ? `\u26A1\xD7${agent.extensions}` : "\u2014" }),
25342
25403
  /* @__PURE__ */ jsx96("span", { className: "text-[9px] text-destructive truncate", title: agent.failureReason, children: agent.failureReason ?? "" })
@@ -27758,6 +27819,9 @@ function feedColor(kind) {
27758
27819
 
27759
27820
  // src/components/OfficeMapCanvas.tsx
27760
27821
  import { Fragment as Fragment22, jsx as jsx104, jsxs as jsxs97 } from "react/jsx-runtime";
27822
+ function clampCtxPct2(value) {
27823
+ return Math.min(100, Math.max(0, value ?? 0));
27824
+ }
27761
27825
  function StatusLED({ status, small, activity = 0 }) {
27762
27826
  const size = small ? "w-2 h-2" : "w-3 h-3";
27763
27827
  const glowRadius = small ? 4 + activity * 4 : 6 + activity * 6;
@@ -28061,6 +28125,7 @@ function AgentNode({ data }) {
28061
28125
  const isError = data.status === "error";
28062
28126
  const isCompleted = data.status === "completed";
28063
28127
  const color = data.color || "#06b6d4";
28128
+ const ctxPct = clampCtxPct2(data.ctxPct);
28064
28129
  return /* @__PURE__ */ jsxs97("div", { className: cn(
28065
28130
  "rounded-lg border p-3 min-w-[150px] transition-all backdrop-blur-sm",
28066
28131
  isActive && "border-cyan-500/50 bg-cyan-500/10 shadow-lg shadow-cyan-500/10",
@@ -28102,15 +28167,15 @@ function AgentNode({ data }) {
28102
28167
  ] })
28103
28168
  ] })
28104
28169
  ] }),
28105
- data.ctxPct != null && data.ctxPct > 0 && /* @__PURE__ */ jsxs97("div", { className: "mb-1", children: [
28170
+ ctxPct > 0 && /* @__PURE__ */ jsxs97("div", { className: "mb-1", children: [
28106
28171
  /* @__PURE__ */ jsxs97("div", { className: "flex justify-between text-[8px] text-gray-500 mb-0.5", children: [
28107
28172
  /* @__PURE__ */ jsx104("span", { children: "ctx" }),
28108
- /* @__PURE__ */ jsxs97("span", { className: cn("font-mono", data.ctxPct >= 90 ? "text-red-400" : data.ctxPct >= 70 ? "text-amber-400" : "text-gray-400"), children: [
28109
- data.ctxPct,
28173
+ /* @__PURE__ */ jsxs97("span", { className: cn("font-mono", ctxPct >= 90 ? "text-red-400" : ctxPct >= 70 ? "text-amber-400" : "text-gray-400"), children: [
28174
+ ctxPct,
28110
28175
  "%"
28111
28176
  ] })
28112
28177
  ] }),
28113
- /* @__PURE__ */ jsx104("div", { className: "h-1 rounded-full bg-slate-700/60 overflow-hidden", children: /* @__PURE__ */ jsx104("div", { className: cn("h-full", data.ctxPct >= 90 ? "bg-red-500" : data.ctxPct >= 70 ? "bg-amber-500" : "bg-cyan-500"), style: { width: `${Math.min(100, data.ctxPct)}%` } }) })
28178
+ /* @__PURE__ */ jsx104("div", { className: "h-1 rounded-full bg-slate-700/60 overflow-hidden", children: /* @__PURE__ */ jsx104("div", { className: cn("h-full", ctxPct >= 90 ? "bg-red-500" : ctxPct >= 70 ? "bg-amber-500" : "bg-cyan-500"), style: { width: `${ctxPct}%` } }) })
28114
28179
  ] }),
28115
28180
  data.lastActivityAt && /* @__PURE__ */ jsxs97("div", { className: cn("flex items-center gap-1 text-[8px]", isActive ? "text-gray-600" : "text-gray-500"), children: [
28116
28181
  isCompleted && /* @__PURE__ */ jsx104("span", { className: "text-emerald-500/70", children: "\u2713 done \xB7" }),
@@ -29067,6 +29132,7 @@ function OfficeMapCanvas() {
29067
29132
  const isAgent = d.kind === "agent";
29068
29133
  const isClient = d.kind === "webui" || d.kind === "tui" || d.kind === "repl";
29069
29134
  const tokTotal = (d.tokensIn || 0) + (d.tokensOut || 0);
29135
+ const ctxPct = clampCtxPct2(d.ctxPct);
29070
29136
  return /* @__PURE__ */ jsxs97("div", { className: "space-y-1.5 text-xs", children: [
29071
29137
  /* @__PURE__ */ jsx104(
29072
29138
  Row,
@@ -29090,7 +29156,7 @@ function OfficeMapCanvas() {
29090
29156
  /* @__PURE__ */ jsx104(Row, { k: "Tokens in", v: fmtCompact(d.tokensIn) }),
29091
29157
  /* @__PURE__ */ jsx104(Row, { k: "Tokens out", v: fmtCompact(d.tokensOut) }),
29092
29158
  /* @__PURE__ */ jsx104(Row, { k: "Tokens total", v: fmtCompact(tokTotal) }),
29093
- d.ctxPct != null && d.ctxPct > 0 && /* @__PURE__ */ jsx104(Row, { k: "Context", v: `${d.ctxPct}%`, accent: d.ctxPct >= 90 ? "text-destructive" : d.ctxPct >= 70 ? "text-amber-600 dark:text-amber-400" : "text-foreground/70" }),
29159
+ ctxPct > 0 && /* @__PURE__ */ jsx104(Row, { k: "Context", v: `${ctxPct}%`, accent: ctxPct >= 90 ? "text-destructive" : ctxPct >= 70 ? "text-amber-600 dark:text-amber-400" : "text-foreground/70" }),
29094
29160
  /* @__PURE__ */ jsx104(Row, { k: "Cost", v: `$${(d.costUsd || 0).toFixed(4)}`, accent: "text-emerald-600 dark:text-emerald-400" }),
29095
29161
  d.lastActivityAt && /* @__PURE__ */ jsx104(Row, { k: "Last seen", v: fmtAgo2(d.lastActivityAt, now), accent: "text-muted-foreground" })
29096
29162
  ] }),