@cesarus777/opencode-cgp 0.4.0 → 0.4.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/tui.js CHANGED
@@ -461,6 +461,51 @@ var init_rpc = __esm(() => {
461
461
  });
462
462
  });
463
463
 
464
+ // src/historyclient.ts
465
+ function historyFailure(error, signal) {
466
+ if (signal?.aborted && signal.reason instanceof DOMException && signal.reason.name === "TimeoutError" || error instanceof DOMException && error.name === "TimeoutError")
467
+ return new HistoryFailure("timeout");
468
+ if (error instanceof HistoryFailure)
469
+ return error;
470
+ const code = typeof error === "object" && error !== null && "type" in error ? error.type : undefined;
471
+ return new HistoryFailure(code === "rpc.unavailable" || code === "rpc.method_not_found" ? "unsupported" : code === "rpc.invalid_output" ? "incompatible" : code === "rpc.invalid_input" ? "rejected" : "unavailable");
472
+ }
473
+ function checkedHistory(value, sessionId) {
474
+ if (!isHistoryResult(value) || value.data.series.some((series) => series.sessionId !== sessionId))
475
+ throw new HistoryFailure("incompatible");
476
+ return value;
477
+ }
478
+ function historyStatusLines(input) {
479
+ const messages = {
480
+ unsupported: "History service is unavailable in this server location. Check that the owning server has a plugin version with chart history. Press r to check again.",
481
+ timeout: "History request timed out. Press r to retry.",
482
+ incompatible: "History response is incompatible. Check client/server plugin versions. Press r to retry.",
483
+ rejected: "History request was rejected. Check client/server plugin versions. Press r to retry.",
484
+ unavailable: "History could not be loaded. Press r to retry."
485
+ };
486
+ return [
487
+ ...input.status === "loading" ? [
488
+ input.loadedAt === undefined ? "Loading retained history\u2026" : "Updating history \xB7 showing previous results"
489
+ ] : input.status === "disabled" ? [
490
+ "History recording is disabled in this server location. Press r to check again after configuration changes."
491
+ ] : input.status === "unavailable" ? [messages[input.reason ?? "unavailable"]] : [],
492
+ ...input.stale && input.loadedAt !== undefined ? [
493
+ `Showing previous results \xB7 last loaded ${new Date(input.loadedAt).toISOString()}`
494
+ ] : []
495
+ ];
496
+ }
497
+ var HistoryFailure;
498
+ var init_historyclient = __esm(() => {
499
+ init_rpc();
500
+ HistoryFailure = class HistoryFailure extends Error {
501
+ reason;
502
+ constructor(reason) {
503
+ super(`History ${reason}`);
504
+ this.reason = reason;
505
+ }
506
+ };
507
+ });
508
+
464
509
  // src/family.ts
465
510
  function metric(snapshot, now, staleMs) {
466
511
  const m = snapshot?.measurement;
@@ -839,6 +884,209 @@ var init_transport = __esm(() => {
839
884
  init_rpc();
840
885
  });
841
886
 
887
+ // src/tui/quantity.ts
888
+ function tokens(value) {
889
+ if (value === undefined || !Number.isFinite(value))
890
+ return "?";
891
+ const magnitude = Math.abs(value);
892
+ if (magnitude >= 1e6)
893
+ return `${+(value / 1e6).toFixed(1)}M`;
894
+ if (magnitude >= 1000)
895
+ return `${+(value / 1000).toFixed(1)}K`;
896
+ return String(value);
897
+ }
898
+
899
+ // src/tui/statusformat.ts
900
+ function freshnessLabel(row2) {
901
+ switch (row2?.freshness) {
902
+ case "fresh":
903
+ return;
904
+ case "stale":
905
+ return "Measurement out of date";
906
+ case "invalid":
907
+ return "Usage unavailable";
908
+ default:
909
+ return "Usage unknown";
910
+ }
911
+ }
912
+ function measurementLabel(row2, ageMs) {
913
+ const m = row2?.snapshot?.measurement;
914
+ if (!m || row2?.freshness === "missing")
915
+ return "Usage unknown";
916
+ if (row2?.freshness === "invalid")
917
+ return "Usage unavailable";
918
+ const age = ageMs === undefined ? "" : ` \xB7 measured ${Math.floor(Math.max(0, ageMs) / 1000)}s ago`;
919
+ return `${m.confidence === "reported" ? "Provider reported" : "Estimated"}${age}${row2?.freshness === "stale" ? " \xB7 out of date" : ""}`;
920
+ }
921
+ function measurementExplanation(row2) {
922
+ const m = row2.snapshot?.measurement;
923
+ if (!m || row2.freshness === "missing")
924
+ return ["No measurement available."];
925
+ if (row2.freshness === "invalid")
926
+ return ["Measurement cannot be used for this session."];
927
+ return [
928
+ m.scope === "completed-request" ? "Usage from the last completed request; not the next request's size." : "Usage for the prepared request; not a completed request.",
929
+ m.provenance === "serialized-estimate" ? "Estimate from serialized request content." : "Provider usage includes input and cache tokens."
930
+ ];
931
+ }
932
+ function compactionLabel(row2, compact = false) {
933
+ const phase = row2?.snapshot?.compaction.phase;
934
+ if (!phase || phase === "idle")
935
+ return;
936
+ if (compact)
937
+ return {
938
+ requesting: "Requesting",
939
+ accepted: "Waiting",
940
+ running: "Compacting",
941
+ "awaiting-measurement": "Checking",
942
+ blocked: "Retry held",
943
+ uncertain: "Unconfirmed"
944
+ }[phase];
945
+ return {
946
+ requesting: "Requesting compaction",
947
+ accepted: "Compaction request accepted \xB7 waiting to start",
948
+ running: "Compaction running",
949
+ "awaiting-measurement": "Compaction ended \xB7 checking context reduction",
950
+ blocked: "Automatic compaction retry suppressed",
951
+ uncertain: "Completion unconfirmed \xB7 automatic retry suppressed"
952
+ }[phase];
953
+ }
954
+ function reasonExplanation(reason) {
955
+ switch (reason) {
956
+ case "below-threshold":
957
+ return "Context is below the policy threshold.";
958
+ case "threshold-crossed":
959
+ return "Context reached the policy threshold; this does not guarantee compaction.";
960
+ case "missing-usage":
961
+ return "No measurement available for the policy decision.";
962
+ case "model-changed":
963
+ return "The measurement belongs to a different model.";
964
+ case "invalid-usage":
965
+ return "The measurement cannot be used for the policy decision.";
966
+ case "stale-usage":
967
+ return "The policy measurement is out of date.";
968
+ case "missing-model-budget":
969
+ return "Model capacity is unavailable for this threshold.";
970
+ case "incompatible-model-budget":
971
+ return "The threshold is incompatible with model capacity.";
972
+ case "compaction-failed":
973
+ return "Compaction failed; automatic retries are limited after repeated failures.";
974
+ case "reclaim-unverifiable":
975
+ return "Context reduction could not be verified with comparable provider readings.";
976
+ case "insufficient-reclaim":
977
+ return "Measured context reduction was below the required minimum.";
978
+ case "admission-outcome-unknown":
979
+ return "Whether the compaction request was accepted is unconfirmed.";
980
+ case "completion-event-timeout":
981
+ return "No confirming completion event arrived within the waiting period.";
982
+ case "restart-needs-lifecycle-evidence":
983
+ return "The previous request needs confirming lifecycle evidence after restart.";
984
+ case "event-stream-gap":
985
+ return "Automatic requests are suspended after an event gap; reload required.";
986
+ case "persistence-unavailable":
987
+ return "Safety storage is unavailable; repair storage and reload.";
988
+ case "capacity-exceeded":
989
+ return "Monitoring limit reached; some sessions are unavailable.";
990
+ case "model-metadata-unavailable":
991
+ return "Some model limits are unavailable.";
992
+ default:
993
+ return "See diagnostic codes.";
994
+ }
995
+ }
996
+ function pressureLabel(row2) {
997
+ if (row2?.freshness !== "fresh")
998
+ return "Threshold pressure unknown";
999
+ switch (row2.pressure) {
1000
+ case "crossed":
1001
+ return "Threshold reached";
1002
+ case "near":
1003
+ return "Near threshold";
1004
+ case "normal":
1005
+ return "Below threshold";
1006
+ default:
1007
+ return "Threshold pressure unknown";
1008
+ }
1009
+ }
1010
+ function thresholdDistance(row2, compact = false) {
1011
+ const delta = row2?.triggerDeltaTokens;
1012
+ if (row2?.freshness !== "fresh" || row2.pressure === "unknown" || delta === undefined || !Number.isFinite(delta))
1013
+ return "pressure unknown";
1014
+ if (delta === 0)
1015
+ return row2.pressure === "crossed" ? "Threshold reached" : "At threshold \xB7 requires more";
1016
+ if (compact)
1017
+ return `${tokens(Math.abs(delta))} ${delta < 0 ? "over" : "below"}${row2.pressure === "near" ? " \xB7 Near threshold" : delta < 0 ? " \xB7 Threshold reached" : ""}`;
1018
+ return `${tokens(Math.abs(delta))} ${Math.abs(delta) === 1 ? "token" : "tokens"} ${delta < 0 ? "over" : "below"} threshold`;
1019
+ }
1020
+ function guardStatusLines(health, compact = false) {
1021
+ switch (health?.enforcement) {
1022
+ case "observing":
1023
+ return compact ? ["Observing", "No Guard compact"] : ["Observing \xB7 Guard won't request compaction"];
1024
+ case "available":
1025
+ return compact ? ["Guard requests", "enabled"] : ["Guard automatic requests enabled"];
1026
+ case "disabled":
1027
+ return ["Guard disabled"];
1028
+ case "suspended": {
1029
+ const gap = health.reasons.includes("event-stream-gap");
1030
+ const storage = health.reasons.includes("persistence-unavailable");
1031
+ return compact ? [
1032
+ "Guard suspended",
1033
+ ...storage ? ["Storage: repair"] : [],
1034
+ ...gap ? ["Reload required"] : storage ? ["Then reload"] : ["See Details"]
1035
+ ] : [
1036
+ "Guard suspended" + (storage ? " \xB7 safety storage unavailable; repair storage and reload" : "") + (gap ? " \xB7 reload required" : storage ? "" : " \xB7 see Details")
1037
+ ];
1038
+ }
1039
+ default:
1040
+ return ["Guard status unknown"];
1041
+ }
1042
+ }
1043
+ function panelStatus(health) {
1044
+ return guardStatusLines(health).join(" \xB7 ");
1045
+ }
1046
+ function connectionLabel(connection, compact = false) {
1047
+ switch (connection) {
1048
+ case "loading":
1049
+ return "Connecting";
1050
+ case "live":
1051
+ return "Live";
1052
+ case "reconnecting":
1053
+ return "Reconnecting";
1054
+ case "unavailable":
1055
+ return compact ? "Unavailable" : "Connection unavailable";
1056
+ case "incompatible":
1057
+ return compact ? "Unsupported" : "Monitoring unsupported";
1058
+ }
1059
+ }
1060
+ function connectionNotice(connection, retainedMeasurements, compact = false) {
1061
+ if (connection === "live")
1062
+ return;
1063
+ return retainedMeasurements ? compact ? "Last data shown" : "Showing last received measurements" : compact ? "No measurements" : "No measurements available";
1064
+ }
1065
+ function monitorStatusLines(connection, health, retainedMeasurements, compact = false) {
1066
+ const notice = connectionNotice(connection, retainedMeasurements, compact);
1067
+ const reasons = health?.reasons ?? [];
1068
+ const limited = reasons.includes("capacity-exceeded");
1069
+ const model = reasons.includes("model-metadata-unavailable");
1070
+ const attention = health?.enforcement !== "suspended" && reasons.length > 0;
1071
+ return [
1072
+ ...health && connection !== "live" ? [compact ? "Last known:" : "Last known Guard:"] : [],
1073
+ ...guardStatusLines(health, compact),
1074
+ ...attention ? compact ? [limited ? "Monitor limit" : model ? "Limits unknown" : "See Details"] : [
1075
+ ...limited ? ["Monitoring limit reached \xB7 some sessions unavailable"] : [],
1076
+ ...model ? ["Some model limits unavailable \xB7 context may be unknown"] : [],
1077
+ ...!limited && !model ? ["Guard needs attention \xB7 see Details"] : []
1078
+ ] : [],
1079
+ ...notice ? [notice] : []
1080
+ ];
1081
+ }
1082
+ var contextHistoryLegend;
1083
+ var init_statusformat = __esm(() => {
1084
+ contextHistoryLegend = [
1085
+ "Recent samples \u2192 now \xB7 \u2504 threshold",
1086
+ "Dim = last known context"
1087
+ ];
1088
+ });
1089
+
842
1090
  // src/tui/format.ts
843
1091
  function quantity(n) {
844
1092
  return n === undefined || !Number.isFinite(n) ? "?" : Math.abs(n) >= 1e6 ? `${(n / 1e6).toFixed(1)}m` : Math.abs(n) >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
@@ -866,22 +1114,6 @@ function clip(value, width) {
866
1114
  }
867
1115
  return width < 3 ? ".".repeat(Math.max(0, width)) : result + "...";
868
1116
  }
869
- function rowLines(row2, width) {
870
- const s = row2.snapshot, m = s?.measurement;
871
- const prefix = row2.current ? "* " : row2.depth > 5 ? ".../" : " ".repeat(Math.min(row2.depth, 5)) + "|-";
872
- const percent = row2.utilizationPercent === undefined ? "?" : `${row2.utilizationPercent.toFixed(1)}%`;
873
- const warning = row2.freshness === "stale" ? " STALE" : row2.pressure === "crossed" ? " !! crossed" : row2.pressure === "near" ? " ! near" : "";
874
- const suffix = ` ${statuses[row2.node.status]} ${m?.confidence === "approximate" ? "~" : ""}${percent}${warning}`;
875
- const label = clip(`${prefix}${row2.node.agent ?? row2.node.sessionId}`, Math.max(1, width - cells(suffix)));
876
- return [
877
- clip(`${label}${suffix}`, width),
878
- ...performanceLines(row2, width),
879
- ...wrap(`${quantity(m?.tokens)}/${quantity(s?.capacityTokens)} input; to ${quantity(row2.triggerDeltaTokens)}`, width),
880
- ...s && s.compaction.phase !== "idle" ? wrap(`compact: ${s.compaction.phase}`, width) : [],
881
- ...row2.node.interrupted ? [clip("interrupted", width)] : [],
882
- ...row2.hidden ? wrap(`+ ${row2.hidden} completed descendants`, width) : []
883
- ];
884
- }
885
1117
  function detailLines(row2, width, expanded = false, ageMs = 0) {
886
1118
  const s = row2?.snapshot, m = s?.measurement;
887
1119
  if (!row2)
@@ -892,22 +1124,29 @@ function detailLines(row2, width, expanded = false, ageMs = 0) {
892
1124
  ...wrap("Guard: not monitored / unavailable", width)
893
1125
  ];
894
1126
  const threshold = s.policy.configured;
895
- const headroom = row2.triggerDeltaTokens;
1127
+ const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
896
1128
  const lines = [
897
1129
  `${s.agent ?? "?"} / ${s.model ?? "?"}`,
1130
+ `${row2.freshness === "stale" ? "Last measured" : "Context"} ${valid2 && m?.confidence === "approximate" ? "~" : ""}${quantity(valid2 ? m?.tokens : undefined)} / ${quantity(s.capacityTokens)} input`,
1131
+ `${valid2 ? row2.utilizationPercent?.toFixed(1) ?? "?" : "?"}% capacity \xB7 ${pressureLabel(row2)}`,
1132
+ `Threshold ${quantity(s.policy.triggerTokens)} \xB7 ${thresholdDistance(row2)}`,
1133
+ measurementLabel(row2, ageMs),
1134
+ ...measurementExplanation(row2),
1135
+ compactionLabel(row2) ?? "No compaction request in progress.",
1136
+ ...s.compaction.blockedReason ? [reasonExplanation(s.compaction.blockedReason)] : [],
1137
+ `Recorded policy decision: ${reasonExplanation(s.policy.reason)}`,
898
1138
  ...performanceLines(row2, width),
899
- `${m?.confidence === "approximate" ? "~" : ""}${quantity(m?.tokens)} / ${quantity(s.capacityTokens)} input`,
900
- `${row2.utilizationPercent?.toFixed(1) ?? "?"}% ${row2.pressure === "crossed" ? "!! crossed" : row2.pressure === "near" ? "! near" : row2.freshness.toUpperCase()}`,
901
- `Trigger ${threshold.type === "ratio" ? `${threshold.value * 100}%` : `${threshold.value} ${threshold.type}`} (${s.policy.comparison})`,
902
- `Resolved trigger ${s.policy.triggerTokens ?? "?"}`,
903
- `Until trigger ${quantity(headroom)}`,
904
- `Compact: ${s.compaction.phase}`,
905
- `${s.compaction.armed ? "armed" : "disarmed"}${s.compaction.blockedReason ? ` / ${s.compaction.blockedReason}` : ""}`,
906
- m ? `${m.confidence} ${m.scope}` : "measurement unknown",
907
- m ? `${Math.floor(Math.max(0, ageMs) / 1000)}s ago; ${row2.freshness}` : ""
1139
+ ...s.performance ? [
1140
+ "TPS: tokens per second. TTFT: time to first output (text or reasoning); approximate.",
1141
+ "Live TPS is estimated; completed rates use provider output including reasoning and matched timing.",
1142
+ "Model average uses matched request durations; session average includes idle and tool time.",
1143
+ ...s.performance.partial ? [
1144
+ "Incomplete timing coverage for model averages; only matched observed durations are included."
1145
+ ] : []
1146
+ ] : []
908
1147
  ];
909
1148
  if (expanded)
910
- lines.push(`Policy ${s.policy.id}: ${s.policy.reason}`, `Limits in/context/out ${quantity(s.limits?.input)}/${quantity(s.limits?.context)}/${quantity(s.limits?.output)}`, `Output reserve ${quantity(s.outputReserveTokens)}`, `Provenance ${m?.provenance ?? "?"}`, `Cooldown until ${s.compaction.cooldownUntil ?? "?"}`, `Cumulative (${s.cumulativeScope})`, `Input ${quantity(s.cumulative?.input)} output ${quantity(s.cumulative?.output)}`, `Reasoning ${quantity(s.cumulative?.reasoning)}`, `Cache read ${quantity(s.cumulative?.cache.read)} write ${quantity(s.cumulative?.cache.write)}`);
1149
+ lines.push("Diagnostic codes and accounting:", `Compaction phase: ${s.compaction.phase}`, `Armed: ${s.compaction.armed} (one eligibility condition, not permission to compact)`, `Blocked reason: ${s.compaction.blockedReason ?? "?"}`, `Measurement confidence: ${m?.confidence ?? "?"}`, `Measurement scope: ${m?.scope ?? "?"}`, `Measurement freshness: ${row2.freshness}`, `Trigger ${threshold.type === "ratio" ? `${threshold.value * 100}%` : `${threshold.value} ${threshold.type}`} (${s.policy.comparison})`, `Resolved trigger ${s.policy.triggerTokens ?? "?"}`, `Policy ${s.policy.id}: ${s.policy.reason}`, `Pressure code: ${row2.pressure}`, `Recorded tokens: ${quantity(m?.tokens)}`, `Limits in/context/out ${quantity(s.limits?.input)}/${quantity(s.limits?.context)}/${quantity(s.limits?.output)}`, `Output reserve ${quantity(s.outputReserveTokens)}`, `Provenance ${m?.provenance ?? "?"}`, `Cooldown until ${s.compaction.cooldownUntil ?? "?"}`, `Cumulative (${s.cumulativeScope})`, `Input ${quantity(s.cumulative?.input)} output ${quantity(s.cumulative?.output)}`, `Reasoning ${quantity(s.cumulative?.reasoning)}`, `Cache read ${quantity(s.cumulative?.cache.read)} write ${quantity(s.cumulative?.cache.write)}`);
911
1150
  return lines.filter(Boolean).flatMap((line) => wrap(line, width));
912
1151
  }
913
1152
  function sanitize(value) {
@@ -944,15 +1183,86 @@ function performanceLines(row2, width) {
944
1183
  ...wrap(`TTFT ${p.ttftMs === undefined ? "?" : `~${(p.ttftMs / 1000).toFixed(2)}s`}${p.latestRequestTps === undefined ? "" : ` \xB7 Last ${value(p.latestRequestTps)} t/s`}`, width)
945
1184
  ];
946
1185
  }
947
- var segmenter, statuses;
1186
+ var segmenter;
948
1187
  var init_format = __esm(() => {
1188
+ init_statusformat();
949
1189
  segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
950
- statuses = {
1190
+ });
1191
+
1192
+ // src/tui/panelformat.ts
1193
+ function panelSummary(row2, width, age, compact = false) {
1194
+ if (!row2)
1195
+ return [clip("No sessions available \xB7 r refresh", width)];
1196
+ const snapshot = row2.snapshot;
1197
+ const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
1198
+ const prefix = row2.freshness === "stale" ? "Last measured" : "Context";
1199
+ const percent = `${row2.utilizationPercent === undefined ? "?" : Math.round(row2.utilizationPercent)}%`;
1200
+ const estimate = valid2 && snapshot?.measurement?.confidence === "approximate" ? "~" : "";
1201
+ const identity = `${width < 28 ? ">" : "Selected:"} ${row2.node.agent ?? row2.node.sessionId}${row2.current ? width < 28 ? " *" : " (current)" : ""}`;
1202
+ const threshold = `Threshold ${tokens(snapshot?.policy.triggerTokens)} \xB7 ${thresholdDistance(row2)}`;
1203
+ const occupancy = compact ? `${prefix} ${estimate}${percent}` : `${prefix} ${estimate}${tokens(valid2 ? snapshot?.measurement?.tokens : undefined)}/${tokens(snapshot?.capacityTokens)} \xB7 ${percent}`;
1204
+ return [
1205
+ clip(identity, width),
1206
+ clip(occupancy, width),
1207
+ ...!compact ? [
1208
+ clip(cells(threshold) <= width ? threshold : thresholdDistance(row2), width),
1209
+ clip(`${row2.node.model ?? snapshot?.model ?? "Model unknown"} \xB7 ${panelStatuses[row2.node.status]}`, width),
1210
+ ...wrap(measurementLabel(row2, age), width)
1211
+ ] : []
1212
+ ];
1213
+ }
1214
+ function tableLine(identity, status, context, threshold, width) {
1215
+ const pad = (value, size) => {
1216
+ const text2 = clip(value, size);
1217
+ return text2 + " ".repeat(Math.max(0, size - cells(text2)));
1218
+ };
1219
+ return `${pad(identity, width - 47)} ${pad(status, 8)} ${pad(context, 7)} ${pad(threshold, 29)}`;
1220
+ }
1221
+ function panelRowHeading(label, width) {
1222
+ return width >= 72 ? tableLine(label, "State", "Context", "Threshold", width) : clip(label, width);
1223
+ }
1224
+ function panelRowLines(row2, width, selected) {
1225
+ const marker = `${selected ? ">" : " "}${row2.current ? "*" : " "}`;
1226
+ const indent = " ".repeat(Math.min(row2.depth, Math.floor(width / 24))) + (row2.depth ? "\u2514 " : "");
1227
+ const identity = `${marker} ${indent}${row2.node.agent ?? row2.node.sessionId}`;
1228
+ const percent = `${(row2.freshness === "fresh" || row2.freshness === "stale") && row2.snapshot?.measurement?.confidence === "approximate" ? "~" : ""}${row2.utilizationPercent === undefined ? "?" : Math.round(row2.utilizationPercent)}%`;
1229
+ const status = width < 28 ? {
951
1230
  running: "RUN",
952
1231
  idle: "IDLE",
953
1232
  completed: "DONE",
954
1233
  failed: "FAIL",
955
1234
  unknown: "?"
1235
+ }[row2.node.status] : panelStatuses[row2.node.status];
1236
+ const distance = thresholdDistance(row2, true);
1237
+ const pad = (value, size) => {
1238
+ const text2 = clip(value, size);
1239
+ return text2 + " ".repeat(Math.max(0, size - cells(text2)));
1240
+ };
1241
+ const lines = width >= 72 ? [tableLine(identity, status, percent, distance, width)] : [
1242
+ `${pad(identity, Math.max(1, width - status.length - 1))} ${status}`,
1243
+ clip(`${percent} context \xB7 ${distance}`, width)
1244
+ ];
1245
+ const exception = [
1246
+ freshnessLabel(row2),
1247
+ compactionLabel(row2),
1248
+ row2.node.interrupted ? "interrupted" : "",
1249
+ row2.hidden ? `+${row2.hidden} hidden \xB7 Space expands` : ""
1250
+ ].filter(Boolean).join(" \xB7 ");
1251
+ if (exception)
1252
+ lines.push(...wrap(exception, width));
1253
+ return lines.map((line) => clip(line, width));
1254
+ }
1255
+ var panelStatuses;
1256
+ var init_panelformat = __esm(() => {
1257
+ init_format();
1258
+ init_statusformat();
1259
+ init_statusformat();
1260
+ panelStatuses = {
1261
+ running: "RUNNING",
1262
+ idle: "IDLE",
1263
+ completed: "DONE",
1264
+ failed: "FAILED",
1265
+ unknown: "UNKNOWN"
956
1266
  };
957
1267
  });
958
1268
 
@@ -1067,15 +1377,15 @@ var init_chartformat = __esm(() => {
1067
1377
 
1068
1378
  // src/tui.tsx
1069
1379
  import { effect as _$effect5 } from "@opentui/solid";
1070
- import { createTextNode as _$createTextNode3 } from "@opentui/solid";
1071
- import { insertNode as _$insertNode5 } from "@opentui/solid";
1380
+ import { insert as _$insert5 } from "@opentui/solid";
1072
1381
  import { setProp as _$setProp5 } from "@opentui/solid";
1073
1382
  import { createElement as _$createElement5 } from "@opentui/solid";
1074
- import { createComponent as _$createComponent5 } from "@opentui/solid";
1383
+ import { createComponent as _$createComponent6 } from "@opentui/solid";
1075
1384
  import { Plugin } from "@opencode/plugin/tui";
1076
1385
  import { Show as Show4 } from "solid-js";
1077
1386
 
1078
1387
  // src/tui/state.ts
1388
+ init_historyclient();
1079
1389
  init_rpc();
1080
1390
  import { createSignal } from "solid-js";
1081
1391
 
@@ -1214,6 +1524,21 @@ function contextChart(samples, capacity, trigger, width, now) {
1214
1524
  init_transport();
1215
1525
 
1216
1526
  // src/tui/inventory.ts
1527
+ function inventoryStatus(inventory, limit, scanning, failed) {
1528
+ const discovered = inventory?.sessions.size ?? 0;
1529
+ const pending = inventory?.pending.length ?? 0;
1530
+ return {
1531
+ discovered,
1532
+ limit,
1533
+ scanning,
1534
+ failed,
1535
+ damaged: inventory?.damaged ?? false,
1536
+ pending,
1537
+ pages: inventory?.pages ?? 0,
1538
+ complete: !failed && (inventory?.complete ?? false),
1539
+ canLoadMore: !scanning && pending > 0 && discovered < 2048
1540
+ };
1541
+ }
1217
1542
  async function discoverFamily(client, currentId, signal, cached, maxNodes = 1024, previous) {
1218
1543
  const sessions = new Map(previous?.sessions);
1219
1544
  let damaged = previous?.damaged ?? false, pages = 0, rootId = previous?.rootId ?? currentId;
@@ -1367,7 +1692,7 @@ function createGuardState(context) {
1367
1692
  inventory = undefined;
1368
1693
  evidence.clear();
1369
1694
  maxNodes = 1024;
1370
- let scanFailed2 = false;
1695
+ scanFailed = false;
1371
1696
  notify();
1372
1697
  scan();
1373
1698
  }
@@ -1382,6 +1707,7 @@ function createGuardState(context) {
1382
1707
  controller.signal,
1383
1708
  AbortSignal.timeout(15000)
1384
1709
  ]);
1710
+ notify();
1385
1711
  try {
1386
1712
  const next = await discoverFamily(context.client, currentId, signal, (id) => context.data.session.get(id), maxNodes, more ? inventory : undefined);
1387
1713
  if (signal.aborted || generation !== scanGeneration)
@@ -1442,8 +1768,10 @@ function createGuardState(context) {
1442
1768
  notify();
1443
1769
  }
1444
1770
  } finally {
1445
- if (generation === scanGeneration)
1771
+ if (generation === scanGeneration) {
1446
1772
  scanning = false;
1773
+ notify();
1774
+ }
1447
1775
  }
1448
1776
  }
1449
1777
  function acquire(id) {
@@ -1555,14 +1883,20 @@ function createGuardState(context) {
1555
1883
  return {
1556
1884
  version,
1557
1885
  acquire,
1558
- async history(id, from, to, signal) {
1886
+ historyLocation(id) {
1887
+ version();
1559
1888
  const info = inventory?.sessions.get(id);
1560
- if (!info)
1561
- throw new Error("Session location unavailable");
1562
- const result = await context.client.rpc(historyRpc).read({ schemaVersion: 1, sessionId: id, from, to, maxPoints: 240 }, { location: info.location, signal });
1563
- if (!isHistoryResult(result))
1564
- throw new Error("Incompatible history service");
1565
- return result;
1889
+ return info ? locationKey(info.location) : "unknown";
1890
+ },
1891
+ async history(id, from, to, signal) {
1892
+ try {
1893
+ const info = inventory?.sessions.get(id);
1894
+ if (!info)
1895
+ throw new Error("Session location unavailable");
1896
+ return checkedHistory(await context.client.rpc(historyRpc).read({ schemaVersion: 1, sessionId: id, from, to, maxPoints: 240 }, { location: info.location, signal }), id);
1897
+ } catch (error) {
1898
+ throw historyFailure(error, signal);
1899
+ }
1566
1900
  },
1567
1901
  rows,
1568
1902
  contextTrace: (row2) => contextTraces.update(row2),
@@ -1586,7 +1920,11 @@ function createGuardState(context) {
1586
1920
  },
1587
1921
  complete() {
1588
1922
  version();
1589
- return inventory?.complete ?? false;
1923
+ return !scanFailed && (inventory?.complete ?? false);
1924
+ },
1925
+ inventoryStatus() {
1926
+ version();
1927
+ return inventoryStatus(inventory, maxNodes, scanning, scanFailed);
1590
1928
  },
1591
1929
  refresh() {
1592
1930
  scan();
@@ -1594,6 +1932,8 @@ function createGuardState(context) {
1594
1932
  c.refresh();
1595
1933
  },
1596
1934
  loadMore() {
1935
+ if (!inventoryStatus(inventory, maxNodes, scanning, scanFailed).canLoadMore)
1936
+ return;
1597
1937
  maxNodes = Math.min(2048, maxNodes + 1024);
1598
1938
  scan(true);
1599
1939
  },
@@ -1620,19 +1960,12 @@ function sameLocation(a, b) {
1620
1960
  }
1621
1961
 
1622
1962
  // src/tui/sidebar.tsx
1623
- init_format();
1624
- import { effect as _$effect2 } from "@opentui/solid";
1625
1963
  import { createComponent as _$createComponent2 } from "@opentui/solid";
1626
- import { insert as _$insert2 } from "@opentui/solid";
1627
- import { createTextNode as _$createTextNode2 } from "@opentui/solid";
1628
- import { insertNode as _$insertNode2 } from "@opentui/solid";
1629
- import { memo as _$memo2 } from "@opentui/solid";
1630
- import { setProp as _$setProp2 } from "@opentui/solid";
1631
- import { createElement as _$createElement2 } from "@opentui/solid";
1632
- import { For as For2, createEffect as createEffect2, createMemo as createMemo2, onCleanup, Show as Show2 } from "solid-js";
1964
+ import { createEffect as createEffect2, createMemo as createMemo2, onCleanup } from "solid-js";
1633
1965
 
1634
1966
  // src/tui/compact.tsx
1635
1967
  init_format();
1968
+ init_statusformat();
1636
1969
  import { createTextNode as _$createTextNode } from "@opentui/solid";
1637
1970
  import { insertNode as _$insertNode } from "@opentui/solid";
1638
1971
  import { setProp as _$setProp } from "@opentui/solid";
@@ -1645,16 +1978,8 @@ import { For, Index, Show, createEffect, createMemo, createSignal as createSigna
1645
1978
 
1646
1979
  // src/tui/compactformat.ts
1647
1980
  init_format();
1648
- function tokens(value) {
1649
- if (value === undefined || !Number.isFinite(value))
1650
- return "?";
1651
- const magnitude = Math.abs(value);
1652
- if (magnitude >= 1e6)
1653
- return `${+(value / 1e6).toFixed(1)}M`;
1654
- if (magnitude >= 1000)
1655
- return `${+(value / 1000).toFixed(1)}K`;
1656
- return String(value);
1657
- }
1981
+ init_panelformat();
1982
+ init_statusformat();
1658
1983
  function columns(left, right, width) {
1659
1984
  if (cells(left) + cells(right) + 1 > width)
1660
1985
  return [...wrap(left, width), ...wrap(right, width)];
@@ -1667,73 +1992,64 @@ function progress(ratio, width) {
1667
1992
  const filled = valid2 ? Math.round(Math.min(1, ratio) * size) : 0;
1668
1993
  return clip("\u2588".repeat(filled) + "\u2591".repeat(size - filled) + " " + label, width);
1669
1994
  }
1670
- function mainSections(row2, width, age, samples, chartCapacity) {
1995
+ function mainSections(row2, width, age) {
1671
1996
  if (!row2)
1672
1997
  return [{ tone: "base", lines: wrap("Loading session family...", width) }];
1673
1998
  const s = row2.snapshot, m = s?.measurement;
1674
1999
  const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
1675
2000
  const measured = valid2 ? m?.tokens : undefined;
1676
- const ratio = (total) => measured !== undefined && total !== undefined && total > 0 ? measured / total : undefined;
1677
- const remaining = measured !== undefined && s?.policy.triggerTokens !== undefined ? s.policy.triggerTokens - measured : undefined;
2001
+ const ratio = measured !== undefined && s?.capacityTokens !== undefined && s.capacityTokens > 0 ? measured / s.capacityTokens : undefined;
2002
+ const lifecycle = compactionLabel(row2);
1678
2003
  return [
1679
- ...row2.snapshot?.performance ? [{ tone: "base", lines: performanceLines(row2, width) }] : [],
1680
2004
  {
1681
2005
  tone: "execution",
1682
2006
  lines: columns(`${row2.node.agent ?? s?.agent ?? "Main"} \xB7 ${row2.node.model ?? s?.model ?? "?"}`, row2.node.status.toUpperCase(), width)
1683
2007
  },
1684
2008
  {
1685
2009
  tone: "base",
1686
- lines: [
1687
- "",
1688
- ...columns("Context", `${tokens(measured)} / ${tokens(s?.capacityTokens)}`, width)
1689
- ]
1690
- },
1691
- ...contextChart(samples ?? (m && valid2 ? [{ at: m.at, tokens: m.tokens }] : []), s?.capacityTokens ?? chartCapacity, s?.policy.triggerTokens, width, m ? m.at + age : s?.observedAt === undefined ? undefined : s.observedAt + age).map(({ line, threshold, segments }) => ({
1692
- tone: threshold ? "threshold" : "pressure",
1693
- lines: [line],
1694
- ...segments ? { segments } : {}
1695
- })),
1696
- ...samples?.some((sample) => sample.held) || m && age > 2500 ? [
1697
- {
1698
- tone: "measurement",
1699
- lines: wrap("muted held \xB7 last known context", width)
1700
- }
1701
- ] : [],
1702
- {
1703
- tone: "base",
1704
- lines: wrap(`${ratio(s?.capacityTokens) === undefined ? "?" : Math.round((ratio(s?.capacityTokens) ?? 0) * 100)}% used \xB7 recent samples \u2192 now`, width)
1705
- },
1706
- {
1707
- tone: "base",
1708
- lines: [
1709
- "",
1710
- ...columns("Compact threshold", `${tokens(measured)} / ${tokens(s?.policy.triggerTokens)}`, width)
1711
- ]
2010
+ lines: columns(row2.freshness === "stale" ? "Last measured" : "Context", `${m?.confidence === "approximate" && valid2 ? "~" : ""}${tokens(measured)} / ${tokens(s?.capacityTokens)}`, width)
1712
2011
  },
2012
+ { tone: "measurement", lines: [progress(ratio, width)] },
1713
2013
  {
1714
2014
  tone: "pressure",
1715
2015
  lines: [
1716
- ...wrap(`${ratio(s?.policy.triggerTokens) === undefined ? "?" : Math.round((ratio(s?.policy.triggerTokens) ?? 0) * 100)}% of threshold \xB7 \u2504 trigger`, width),
1717
- ...s?.capacityTokens !== undefined && s.policy.triggerTokens !== undefined && s.policy.triggerTokens > s.capacityTokens ? wrap("Threshold above chart capacity", width) : [],
1718
- ...wrap(remaining === undefined ? "Compaction distance unknown" : remaining < 0 ? `${tokens(-remaining)} tokens over threshold` : `${tokens(remaining)} tokens until compaction`, width)
2016
+ ...wrap(thresholdDistance(row2), width),
2017
+ ...["near", "crossed"].includes(row2.pressure) && row2.freshness === "fresh" ? wrap(pressureLabel(row2), width) : [],
2018
+ ...s?.capacityTokens !== undefined && s.policy.triggerTokens !== undefined && s.policy.triggerTokens > s.capacityTokens ? wrap("Threshold above capacity", width) : []
1719
2019
  ]
1720
2020
  },
1721
2021
  {
1722
2022
  tone: "measurement",
1723
- lines: [
1724
- "",
1725
- ...wrap(`Measurement: ${m?.confidence ?? "unknown"}${m ? ` \xB7 ${Math.floor(Math.max(0, age) / 1000)}s ago` : ""}${row2.freshness !== "fresh" ? ` \xB7 ${row2.freshness.toUpperCase()}` : ""}`, width)
1726
- ]
2023
+ lines: wrap(measurementLabel(row2, age), width)
1727
2024
  },
1728
2025
  {
1729
2026
  tone: "base",
1730
2027
  lines: [
1731
- ...s && s.compaction.phase !== "idle" ? wrap(`Compact: ${s.compaction.phase}`, width) : [],
2028
+ ...lifecycle ? wrap(lifecycle, width) : [],
2029
+ ...row2.node.interrupted ? wrap("Interrupted", width) : [],
1732
2030
  ...!s ? wrap("Guard: not monitored / unavailable", width) : []
1733
2031
  ]
1734
2032
  }
1735
2033
  ];
1736
2034
  }
2035
+ function historySections(row2, width, age, samples, chartCapacity) {
2036
+ const s = row2?.snapshot, m = s?.measurement;
2037
+ return [
2038
+ ...contextChart(samples, s?.capacityTokens ?? chartCapacity, s?.policy.triggerTokens, width, m ? m.at + age : s ? s.observedAt + age : undefined).map(({ line, threshold, segments }) => ({
2039
+ tone: threshold ? "threshold" : "measurement",
2040
+ lines: [line],
2041
+ ...segments ? { segments } : {}
2042
+ })),
2043
+ {
2044
+ tone: "measurement",
2045
+ lines: contextHistoryLegend.flatMap((line) => wrap(line, width))
2046
+ },
2047
+ {
2048
+ tone: "pressure",
2049
+ lines: wrap(`Policy threshold ${tokens(s?.policy.triggerTokens)} \xB7 ${row2?.freshness === "fresh" && row2.pressureRatio !== undefined ? Math.round(row2.pressureRatio * 100) : "?"}% of threshold`, width)
2050
+ }
2051
+ ];
2052
+ }
1737
2053
  var statusSymbol = {
1738
2054
  running: "\u25CF",
1739
2055
  idle: "\u25CC",
@@ -1742,27 +2058,31 @@ var statusSymbol = {
1742
2058
  failed: "\u2715"
1743
2059
  };
1744
2060
  function agentHeading(row2, width) {
1745
- const name = `${statusSymbol[row2.node.status]} ${row2.node.agent ?? row2.node.sessionId}`;
1746
- const percent = row2.utilizationPercent === undefined ? "?%" : `${Math.round(row2.utilizationPercent)}%`;
1747
- return cells(name) + cells(percent) + 1 <= width ? columns(name, percent, width)[0] ?? "" : clip(name, width);
2061
+ return clip(`${statusSymbol[row2.node.status]} ${row2.node.agent ?? row2.node.sessionId}`, width);
1748
2062
  }
1749
2063
  function agentLines(row2, width) {
1750
2064
  const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
1751
- const measured = valid2 ? row2.snapshot?.measurement?.tokens : undefined;
1752
- const capacity = row2.snapshot?.capacityTokens;
1753
- const pressure = row2.pressure === "crossed" ? "CRITICAL" : row2.pressure === "near" ? "WARNING" : "";
2065
+ const percent = valid2 && row2.utilizationPercent !== undefined ? `${Math.round(row2.utilizationPercent)}%` : "?%";
2066
+ const estimate = valid2 && row2.snapshot?.measurement?.confidence === "approximate" ? "~" : "";
2067
+ const context = `${row2.freshness === "stale" ? "Last measured" : "Context"} ${estimate}${percent}`;
2068
+ const pressure = pressureLabel(row2);
2069
+ const full = `${context} \xB7 ${pressure}`;
2070
+ const short = `${row2.freshness === "stale" ? "Last measured" : "Ctx"} ${estimate}${percent} \xB7 ${pressure === "Below threshold" ? "Threshold below" : pressure}`;
2071
+ const lifecycle = compactionLabel(row2);
1754
2072
  return [
1755
- ...performanceLines(row2, width),
1756
- progress(measured !== undefined && capacity !== undefined && capacity > 0 ? measured / capacity : undefined, width),
1757
- ...wrap(`${tokens(measured)} / ${tokens(capacity)}${pressure ? ` \xB7 ${pressure}` : ""}`, width),
1758
- ...wrap(`${row2.node.status.toUpperCase()}${row2.freshness !== "fresh" ? ` \xB7 ${row2.freshness.toUpperCase()}` : ""}${row2.node.interrupted ? " \xB7 interrupted" : ""}`, width),
1759
- ...row2.snapshot && row2.snapshot.compaction.phase !== "idle" ? wrap(`Compact: ${row2.snapshot.compaction.phase}`, width) : []
2073
+ ...wrap(cells(full) <= width ? full : short, width),
2074
+ ...row2.node.status === "failed" || row2.node.status === "unknown" ? wrap(row2.node.status.toUpperCase(), width) : [],
2075
+ ...row2.freshness !== "fresh" ? wrap(freshnessLabel(row2) ?? "", width) : [],
2076
+ ...row2.node.interrupted ? wrap("Interrupted", width) : [],
2077
+ ...lifecycle ? wrap(lifecycle, width) : []
1760
2078
  ];
1761
2079
  }
1762
- function subagentTree(rows, sessionID, collapsed, hideCompleted, byPressure) {
2080
+ function subagentTree(rows, sessionID, collapsed, hideCompleted, byPressure, trustworthy = true) {
1763
2081
  const children = new Map;
1764
- for (const row2 of rows) {
1765
- if (!row2.node.parentId || row2.node.sessionId === sessionID)
2082
+ const unique = new Map(rows.map((row2) => [row2.node.sessionId, row2]));
2083
+ let complete = unique.size <= 2048;
2084
+ for (const row2 of [...unique.values()].slice(0, 2048)) {
2085
+ if (!row2.node.parentId)
1766
2086
  continue;
1767
2087
  const list = children.get(row2.node.parentId) ?? [];
1768
2088
  list.push(row2);
@@ -1771,41 +2091,79 @@ function subagentTree(rows, sessionID, collapsed, hideCompleted, byPressure) {
1771
2091
  const seen = new Set([sessionID]);
1772
2092
  const all = [];
1773
2093
  const build = (id, depth) => {
1774
- if (depth > 64)
1775
- return [];
2094
+ if (depth > 64) {
2095
+ const safe2 = !children.get(id)?.length;
2096
+ if (!safe2)
2097
+ complete = false;
2098
+ return { branches: [], safe: safe2 };
2099
+ }
2100
+ let safe = true;
1776
2101
  const branches2 = [];
1777
2102
  for (const row2 of children.get(id) ?? []) {
1778
- if (seen.has(row2.node.sessionId))
2103
+ if (seen.has(row2.node.sessionId)) {
2104
+ safe = false;
2105
+ complete = false;
1779
2106
  continue;
2107
+ }
1780
2108
  seen.add(row2.node.sessionId);
1781
2109
  all.push(row2);
1782
2110
  const nested = build(row2.node.sessionId, depth + 1);
1783
- if (hideCompleted && row2.node.status === "completed" && !nested.length)
1784
- continue;
2111
+ const branchSafe = nested.safe && row2.hidden === 0;
2112
+ safe &&= branchSafe;
2113
+ complete &&= branchSafe;
2114
+ const eligible = trustworthy && unique.size <= 2048 && branchSafe && row2.node.status === "completed" && row2.freshness === "fresh" && row2.pressure === "normal" && row2.snapshot?.compaction.phase === "idle" && !row2.node.interrupted && nested.branches.every((b) => b.eligible);
1785
2115
  branches2.push({
1786
2116
  row: row2,
1787
- children: nested,
1788
- active: row2.node.status === "running" || nested.some((b) => b.active),
1789
- pressure: Math.max(row2.pressureRatio ?? -1, ...nested.map((b) => b.pressure))
2117
+ children: nested.branches,
2118
+ eligible,
2119
+ active: row2.node.status === "running" || nested.branches.some((b) => b.active),
2120
+ pressure: Math.max(row2.pressureRatio ?? -1, ...nested.branches.map((b) => b.pressure)),
2121
+ size: 1 + nested.branches.reduce((sum, b) => sum + b.size, 0),
2122
+ attention: !eligible || nested.branches.some((b) => b.attention)
1790
2123
  });
1791
2124
  }
1792
- return branches2.sort((a, b) => Number(b.active) - Number(a.active) || (byPressure ? b.pressure - a.pressure : 0));
2125
+ return {
2126
+ safe,
2127
+ branches: branches2.sort((a, b) => Number(b.active) - Number(a.active) || (byPressure ? b.pressure - a.pressure : 0))
2128
+ };
1793
2129
  };
1794
- const branches = build(sessionID, 0);
2130
+ const { branches } = build(sessionID, 0);
1795
2131
  const visible = [];
2132
+ let grouped = 0;
2133
+ const count2 = (items) => {
2134
+ for (const item of items) {
2135
+ if (item.eligible)
2136
+ grouped++;
2137
+ count2(item.children);
2138
+ }
2139
+ };
2140
+ count2(branches);
1796
2141
  const visit = (items, depth) => {
1797
2142
  for (const item of items) {
2143
+ if (hideCompleted && item.eligible)
2144
+ continue;
2145
+ const folded = collapsed.has(item.row.node.sessionId);
1798
2146
  visible.push({
1799
2147
  row: item.row,
1800
2148
  depth,
1801
- hasChildren: !!item.children.length
2149
+ hasChildren: !!item.children.length,
2150
+ hidden: folded ? item.size - 1 : 0,
2151
+ attention: folded && item.children.some((b) => b.attention)
1802
2152
  });
1803
- if (!collapsed.has(item.row.node.sessionId))
2153
+ if (!folded)
1804
2154
  visit(item.children, depth + 1);
1805
2155
  }
1806
2156
  };
1807
2157
  visit(branches, 0);
1808
- return { all, visible };
2158
+ return { all, visible, grouped, complete };
2159
+ }
2160
+ function routeFamily(rows, sessionID) {
2161
+ const tree = subagentTree(rows, sessionID, new Set, false, false, false);
2162
+ const root = rows.find((row2) => row2.node.sessionId === sessionID);
2163
+ return {
2164
+ rows: [...root ? [root] : [], ...tree.all],
2165
+ complete: !!root && root.hidden === 0 && tree.complete
2166
+ };
1809
2167
  }
1810
2168
  function statistics(rows, sessionID, complete, width) {
1811
2169
  const sum = (value) => {
@@ -1825,15 +2183,19 @@ function CompactSidebar(props) {
1825
2183
  const [measuredWidth, setMeasuredWidth] = createSignal2(props.width);
1826
2184
  const width = () => Math.max(1, Math.min(props.width, measuredWidth()) - 4);
1827
2185
  const [collapsed, setCollapsed] = createSignal2(new Set);
1828
- const [hideCompleted, setHideCompleted] = createSignal2(false);
2186
+ const [hideCompleted, setHideCompleted] = createSignal2(true);
2187
+ const [showHistory, setShowHistory] = createSignal2(false);
1829
2188
  const [byPressure, setByPressure] = createSignal2(true);
1830
2189
  createEffect(() => {
1831
2190
  props.sessionID;
1832
2191
  setCollapsed(new Set);
2192
+ setShowHistory(false);
1833
2193
  });
1834
2194
  const current = createMemo(() => props.rows.find((r) => r.node.sessionId === props.sessionID));
1835
2195
  const trace = createMemo(() => props.state.contextTrace(current()));
1836
- const tree = createMemo(() => subagentTree(props.rows, props.sessionID, collapsed(), hideCompleted(), byPressure()));
2196
+ const inventory = createMemo(() => props.state.inventoryStatus());
2197
+ const trustworthy = () => inventory().complete && !inventory().scanning && !inventory().failed && !inventory().damaged;
2198
+ const tree = createMemo(() => subagentTree(props.rows, props.sessionID, collapsed(), hideCompleted(), byPressure(), trustworthy()));
1837
2199
  const entries = createMemo(() => new Map(tree().visible.map((entry) => [entry.row.node.sessionId, entry])));
1838
2200
  const toggle = (id) => setCollapsed((previous) => {
1839
2201
  const next = new Set(previous);
@@ -1844,13 +2206,21 @@ function CompactSidebar(props) {
1844
2206
  return next;
1845
2207
  });
1846
2208
  const toggleCompleted = () => setHideCompleted((value) => !value);
2209
+ const toggleHistory = () => setShowHistory((value) => !value);
1847
2210
  const toggleSort = () => setByPressure((value) => !value);
1848
2211
  const toggleGroups = () => setCollapsed((previous) => previous.size ? new Set : new Set(tree().all.map((r) => r.node.sessionId)));
1849
2212
  props.context.keymap.layer(() => ({
1850
2213
  mode: "global",
1851
2214
  commands: [{
2215
+ id: "context-guard.history",
2216
+ title: "Show or hide sidebar context history",
2217
+ group: "Context Guard",
2218
+ palette: true,
2219
+ bind: false,
2220
+ run: toggleHistory
2221
+ }, {
1852
2222
  id: "context-guard.completed",
1853
- title: "Toggle completed subagents",
2223
+ title: "Show or hide completed subagent group",
1854
2224
  group: "Context Guard",
1855
2225
  palette: true,
1856
2226
  bind: false,
@@ -1894,45 +2264,21 @@ function CompactSidebar(props) {
1894
2264
  })()
1895
2265
  });
1896
2266
  }
1897
- const section = (title) => {
1898
- const head = cells(`\u2500\u2500 ${title} `) > width() ? title : `\u2500\u2500 ${title} `;
1899
- return clip(head, width()) + "\u2500".repeat(Math.max(0, width() - cells(head)));
1900
- };
1901
- const notices = createMemo(() => [...!["2.0.29", "2.1.2"].includes(props.context.app.version) ? wrap(`Runtime ${props.context.app.version}: unverified`, width()) : [], ...props.state.connection() !== "live" ? wrap(`Connection: ${props.state.connection()}`, width()) : [], ...props.state.health()?.mode === "observe" ? ["Observe mode"] : [], ...props.state.health()?.enforcement === "disabled" || props.state.health()?.enforcement === "suspended" ? wrap(`Guard: ${props.state.health()?.enforcement}`, width()) : [], ...(props.state.health()?.reasons ?? []).flatMap((reason) => wrap(reason, width()))]);
1902
- return (() => {
1903
- var _el$2 = _$createElement("box"), _el$3 = _$createElement("text"), _el$5 = _$createElement("text"), _el$6 = _$createElement("text"), _el$8 = _$createElement("text"), _el$9 = _$createElement("text"), _el$0 = _$createElement("text"), _el$1 = _$createElement("text"), _el$11 = _$createElement("text"), _el$12 = _$createElement("text");
1904
- _$insertNode(_el$2, _el$3);
1905
- _$insertNode(_el$2, _el$5);
1906
- _$insertNode(_el$2, _el$6);
1907
- _$insertNode(_el$2, _el$8);
1908
- _$insertNode(_el$2, _el$9);
1909
- _$insertNode(_el$2, _el$0);
1910
- _$insertNode(_el$2, _el$1);
1911
- _$insertNode(_el$2, _el$11);
1912
- _$insertNode(_el$2, _el$12);
1913
- _$setProp(_el$2, "width", "100%");
1914
- _$setProp(_el$2, "flexShrink", 0);
1915
- _$setProp(_el$2, "border", true);
1916
- _$setProp(_el$2, "borderStyle", "rounded");
1917
- _$setProp(_el$2, "title", " CONTEXT GUARD ");
1918
- _$setProp(_el$2, "paddingX", 1);
1919
- _$setProp(_el$2, "flexDirection", "column");
1920
- _$setProp(_el$2, "onSizeChange", function() {
1921
- setMeasuredWidth(this.width);
1922
- });
1923
- _$insert(_el$2, _$createComponent(Index, {
2267
+ const notices = createMemo(() => [...!["2.0.29", "2.1.2"].includes(props.context.app.version) ? wrap(`Runtime ${props.context.app.version}: unverified`, width()) : [], ...[connectionLabel(props.state.connection(), width() < 28), ...monitorStatusLines(props.state.connection(), props.state.health(), props.rows.some((row2) => row2.snapshot?.measurement !== undefined), width() < 28)].flatMap((line) => wrap(line, width()))]);
2268
+ function Sections(input) {
2269
+ return _$createComponent(Index, {
1924
2270
  get each() {
1925
- return mainSections(current(), width(), props.state.age(props.sessionID), trace().samples, trace().capacity);
2271
+ return input.sections;
1926
2272
  },
1927
- children: (section2) => _$createComponent(Index, {
2273
+ children: (section) => _$createComponent(Index, {
1928
2274
  get each() {
1929
- return section2().lines;
2275
+ return section().lines;
1930
2276
  },
1931
2277
  children: (line) => (() => {
1932
- var _el$13 = _$createElement("text");
1933
- _$insert(_el$13, _$createComponent(Show, {
2278
+ var _el$2 = _$createElement("text");
2279
+ _$insert(_el$2, _$createComponent(Show, {
1934
2280
  get when() {
1935
- return section2().segments;
2281
+ return section().segments;
1936
2282
  },
1937
2283
  get fallback() {
1938
2284
  return line() || " ";
@@ -1940,51 +2286,86 @@ function CompactSidebar(props) {
1940
2286
  get children() {
1941
2287
  return _$createComponent(For, {
1942
2288
  get each() {
1943
- return section2().segments;
2289
+ return section().segments;
1944
2290
  },
1945
2291
  children: (part) => (() => {
1946
- var _el$14 = _$createElement("span");
1947
- _$insert(_el$14, () => part.text);
1948
- _$effect((_$p) => _$setProp(_el$14, "style", {
2292
+ var _el$3 = _$createElement("span");
2293
+ _$insert(_el$3, () => part.text);
2294
+ _$effect((_$p) => _$setProp(_el$3, "style", {
1949
2295
  fg: part.tone === "axis" || part.tone === "held" ? colors().muted : part.tone === "threshold" ? colors().feedback.warning.base : current()?.freshness === "fresh" ? colors().base : colors().muted
1950
2296
  }, _$p));
1951
- return _el$14;
2297
+ return _el$3;
1952
2298
  })()
1953
2299
  });
1954
2300
  }
1955
2301
  }));
1956
- _$effect((_$p) => _$setProp(_el$13, "fg", section2().tone === "execution" ? executionColor(current()) : section2().tone === "threshold" ? colors().feedback.warning.base : section2().tone === "pressure" ? pressureColor(current()) : section2().tone === "measurement" ? current()?.freshness === "fresh" ? colors().muted : colors().feedback.warning.base : colors().base, _$p));
1957
- return _el$13;
2302
+ _$effect((_$p) => _$setProp(_el$2, "fg", section().tone === "execution" ? executionColor(current()) : section().tone === "threshold" ? colors().feedback.warning.base : section().tone === "pressure" ? pressureColor(current()) : section().tone === "measurement" ? current()?.freshness === "fresh" ? colors().muted : colors().feedback.warning.base : colors().base, _$p));
2303
+ return _el$2;
1958
2304
  })()
1959
2305
  })
1960
- }), _el$3);
1961
- _$insert(_el$2, _$createComponent(Index, {
2306
+ });
2307
+ }
2308
+ return (() => {
2309
+ var _el$4 = _$createElement("box"), _el$5 = _$createElement("text"), _el$6 = _$createElement("text"), _el$8 = _$createElement("text"), _el$9 = _$createElement("text");
2310
+ _$insertNode(_el$4, _el$5);
2311
+ _$insertNode(_el$4, _el$6);
2312
+ _$insertNode(_el$4, _el$8);
2313
+ _$insertNode(_el$4, _el$9);
2314
+ _$setProp(_el$4, "width", "100%");
2315
+ _$setProp(_el$4, "flexShrink", 0);
2316
+ _$setProp(_el$4, "border", true);
2317
+ _$setProp(_el$4, "borderStyle", "rounded");
2318
+ _$setProp(_el$4, "title", " CONTEXT GUARD ");
2319
+ _$setProp(_el$4, "paddingX", 1);
2320
+ _$setProp(_el$4, "flexDirection", "column");
2321
+ _$setProp(_el$4, "onSizeChange", function() {
2322
+ setMeasuredWidth(this.width);
2323
+ });
2324
+ _$insert(_el$4, _$createComponent(Sections, {
2325
+ get sections() {
2326
+ return mainSections(current(), width(), props.state.age(props.sessionID));
2327
+ }
2328
+ }), _el$5);
2329
+ _$insert(_el$4, _$createComponent(Index, {
1962
2330
  get each() {
1963
2331
  return notices();
1964
2332
  },
1965
2333
  children: (line) => (() => {
1966
- var _el$15 = _$createElement("text");
1967
- _$insert(_el$15, line);
1968
- _$effect((_$p) => _$setProp(_el$15, "fg", colors().feedback.warning.base, _$p));
1969
- return _el$15;
2334
+ var _el$0 = _$createElement("text");
2335
+ _$insert(_el$0, line);
2336
+ _$effect((_$p) => _$setProp(_el$0, "fg", colors().feedback.warning.base, _$p));
2337
+ return _el$0;
1970
2338
  })()
1971
- }), _el$3);
1972
- _$insertNode(_el$3, _$createTextNode(` `));
1973
- _$insert(_el$5, () => section(`SUBAGENTS (${tree().all.length})`));
1974
- _$insert(_el$2, _$createComponent(Lines, {
2339
+ }), _el$5);
2340
+ _$setProp(_el$5, "onMouseDown", () => props.context.ui.panel.open("context-guard.monitor"));
2341
+ _$insert(_el$5, () => clip("Open: /context-guard", width()));
2342
+ _$setProp(_el$6, "onMouseDown", toggleHistory);
2343
+ _$insert(_el$6, () => clip(showHistory() ? "History: hide" : "History: show", width()));
2344
+ _$insert(_el$4, _$createComponent(Show, {
2345
+ get when() {
2346
+ return showHistory();
2347
+ },
2348
+ get children() {
2349
+ return _$createComponent(Sections, {
2350
+ get sections() {
2351
+ return historySections(current(), width(), props.state.age(props.sessionID), trace().samples, trace().capacity);
2352
+ }
2353
+ });
2354
+ }
2355
+ }), _el$8);
2356
+ _$insert(_el$4, _$createComponent(Lines, {
1975
2357
  get lines() {
1976
- return wrap(`${tree().all.filter((r) => r.node.status === "running").length} running \xB7 ${tree().all.filter((r) => r.node.status === "completed").length} completed${props.state.complete() ? "" : " \xB7 partial"}`, width());
2358
+ return wrap(`SUBAGENTS (${tree().all.length}) \xB7 ${tree().all.filter((r) => r.node.status === "running").length} running${trustworthy() && tree().complete ? "" : " \xB7 partial"}`, width());
1977
2359
  }
1978
- }), _el$6);
1979
- _$insertNode(_el$6, _$createTextNode(` `));
1980
- _$insert(_el$2, _$createComponent(For, {
2360
+ }), _el$8);
2361
+ _$insert(_el$4, _$createComponent(For, {
1981
2362
  get each() {
1982
2363
  return tree().visible.map((entry) => entry.row.node.sessionId);
1983
2364
  },
1984
2365
  children: (id) => {
1985
2366
  const entry = () => entries().get(id);
1986
2367
  const row2 = () => entry()?.row;
1987
- const indent = () => " ".repeat(Math.min(entry()?.depth ?? 0, Math.floor(width() / 6)) * 2);
2368
+ const indent = () => " ".repeat(Math.min((entry()?.depth ?? 0) * 2, Math.max(0, width() - 16)));
1988
2369
  const prefix = () => `${indent()}${entry()?.depth ? "\u2514\u2500 " : ""}${entry()?.hasChildren ? collapsed().has(id) ? "\u25B8 " : "\u25BE " : ""}`;
1989
2370
  const nameWidth = () => Math.max(1, width() - cells(prefix()));
1990
2371
  return _$createComponent(Show, {
@@ -1992,90 +2373,99 @@ function CompactSidebar(props) {
1992
2373
  return row2();
1993
2374
  },
1994
2375
  children: (item) => (() => {
1995
- var _el$16 = _$createElement("box"), _el$17 = _$createElement("box"), _el$19 = _$createElement("text"), _el$20 = _$createElement("span"), _el$21 = _$createElement("span"), _el$22 = _$createElement("text");
1996
- _$insertNode(_el$16, _el$17);
1997
- _$insertNode(_el$16, _el$22);
1998
- _$setProp(_el$16, "flexDirection", "column");
1999
- _$setProp(_el$16, "flexShrink", 0);
2000
- _$insertNode(_el$17, _el$19);
2001
- _$setProp(_el$17, "flexDirection", "row");
2002
- _$setProp(_el$17, "flexShrink", 0);
2003
- _$insert(_el$17, _$createComponent(Show, {
2376
+ var _el$1 = _$createElement("box"), _el$10 = _$createElement("box"), _el$12 = _$createElement("text"), _el$13 = _$createElement("span"), _el$14 = _$createElement("span");
2377
+ _$insertNode(_el$1, _el$10);
2378
+ _$setProp(_el$1, "flexDirection", "column");
2379
+ _$setProp(_el$1, "flexShrink", 0);
2380
+ _$insertNode(_el$10, _el$12);
2381
+ _$setProp(_el$10, "flexDirection", "row");
2382
+ _$setProp(_el$10, "flexShrink", 0);
2383
+ _$insert(_el$10, _$createComponent(Show, {
2004
2384
  get when() {
2005
2385
  return prefix();
2006
2386
  },
2007
2387
  get children() {
2008
- var _el$18 = _$createElement("text");
2009
- _$setProp(_el$18, "onMouseDown", () => toggle(id));
2010
- _$insert(_el$18, prefix);
2011
- _$effect((_$p) => _$setProp(_el$18, "fg", colors().muted, _$p));
2012
- return _el$18;
2388
+ var _el$11 = _$createElement("text");
2389
+ _$setProp(_el$11, "onMouseDown", () => toggle(id));
2390
+ _$insert(_el$11, prefix);
2391
+ _$effect((_$p) => _$setProp(_el$11, "fg", colors().muted, _$p));
2392
+ return _el$11;
2013
2393
  }
2014
- }), _el$19);
2015
- _$insertNode(_el$19, _el$20);
2016
- _$insertNode(_el$19, _el$21);
2017
- _$setProp(_el$19, "onMouseDown", () => props.context.ui.router.navigate({
2394
+ }), _el$12);
2395
+ _$insertNode(_el$12, _el$13);
2396
+ _$insertNode(_el$12, _el$14);
2397
+ _$setProp(_el$12, "onMouseDown", () => props.context.ui.router.navigate({
2018
2398
  type: "session",
2019
2399
  sessionID: id
2020
2400
  }));
2021
- _$insert(_el$20, () => agentHeading(item(), nameWidth()).slice(0, 1));
2022
- _$insert(_el$21, () => agentHeading(item(), nameWidth()).slice(1));
2023
- _$insert(_el$16, _$createComponent(Index, {
2401
+ _$insert(_el$13, () => agentHeading(item(), nameWidth()).slice(0, 1));
2402
+ _$insert(_el$14, () => agentHeading(item(), nameWidth()).slice(1));
2403
+ _$insert(_el$1, _$createComponent(Index, {
2024
2404
  get each() {
2025
2405
  return agentLines(item(), Math.max(1, width() - cells(indent()) - 2));
2026
2406
  },
2027
2407
  children: (line) => (() => {
2028
- var _el$24 = _$createElement("text"), _el$25 = _$createTextNode(` `);
2029
- _$insertNode(_el$24, _el$25);
2030
- _$insert(_el$24, indent, _el$25);
2031
- _$insert(_el$24, line, null);
2032
- _$effect((_$p) => _$setProp(_el$24, "fg", rowColor(item()), _$p));
2033
- return _el$24;
2408
+ var _el$15 = _$createElement("text"), _el$16 = _$createTextNode(` `);
2409
+ _$insertNode(_el$15, _el$16);
2410
+ _$insert(_el$15, indent, _el$16);
2411
+ _$insert(_el$15, line, null);
2412
+ _$effect((_$p) => _$setProp(_el$15, "fg", rowColor(item()), _$p));
2413
+ return _el$15;
2034
2414
  })()
2035
- }), _el$22);
2036
- _$insertNode(_el$22, _$createTextNode(` `));
2415
+ }), null);
2416
+ _$insert(_el$1, _$createComponent(Show, {
2417
+ get when() {
2418
+ return entry()?.hidden;
2419
+ },
2420
+ get children() {
2421
+ return _$createComponent(Lines, {
2422
+ get lines() {
2423
+ return wrap(`${entry()?.hidden} hidden${entry()?.attention ? " \xB7 needs attention" : ""}`, width());
2424
+ }
2425
+ });
2426
+ }
2427
+ }), null);
2037
2428
  _$effect((_p$) => {
2038
- var _v$0 = rowColor(item()), _v$1 = {
2429
+ var _v$8 = rowColor(item()), _v$9 = {
2039
2430
  fg: executionColor(item())
2040
2431
  };
2041
- _v$0 !== _p$.e && (_p$.e = _$setProp(_el$19, "fg", _v$0, _p$.e));
2042
- _v$1 !== _p$.t && (_p$.t = _$setProp(_el$20, "style", _v$1, _p$.t));
2432
+ _v$8 !== _p$.e && (_p$.e = _$setProp(_el$12, "fg", _v$8, _p$.e));
2433
+ _v$9 !== _p$.t && (_p$.t = _$setProp(_el$13, "style", _v$9, _p$.t));
2043
2434
  return _p$;
2044
2435
  }, {
2045
2436
  e: undefined,
2046
2437
  t: undefined
2047
2438
  });
2048
- return _el$16;
2439
+ return _el$1;
2049
2440
  })()
2050
2441
  });
2051
2442
  }
2052
2443
  }), _el$8);
2053
- _$setProp(_el$8, "onMouseDown", toggleCompleted);
2054
- _$insert(_el$8, () => clip(hideCompleted() ? "Show completed" : "Hide completed", width()));
2055
- _$setProp(_el$9, "onMouseDown", toggleSort);
2056
- _$insert(_el$9, () => clip(byPressure() ? "Sort: pressure" : "Sort: activity", width()));
2057
- _$setProp(_el$0, "onMouseDown", toggleGroups);
2058
- _$insert(_el$0, () => clip(collapsed().size ? "Expand groups" : "Collapse groups", width()));
2059
- _$insertNode(_el$1, _$createTextNode(` `));
2060
- _$insert(_el$11, () => section("STATISTICS"));
2061
- _$insert(_el$2, _$createComponent(Lines, {
2062
- get lines() {
2063
- return statistics(props.rows.filter((r) => r.node.sessionId === props.sessionID).concat(tree().all), props.sessionID, props.state.complete(), width());
2444
+ _$insert(_el$4, _$createComponent(Show, {
2445
+ get when() {
2446
+ return tree().grouped > 0;
2447
+ },
2448
+ get children() {
2449
+ var _el$7 = _$createElement("text");
2450
+ _$setProp(_el$7, "onMouseDown", toggleCompleted);
2451
+ _$insert(_el$7, () => clip(`Completed (${tree().grouped}): ${hideCompleted() ? "show" : "hide"}`, width()));
2452
+ _$effect((_$p) => _$setProp(_el$7, "fg", colors().muted, _$p));
2453
+ return _el$7;
2064
2454
  }
2065
- }), _el$12);
2066
- _$setProp(_el$12, "onMouseDown", () => props.context.ui.panel.open("context-guard.monitor"));
2067
- _$insert(_el$12, () => clip("Open: /context-guard", width()));
2455
+ }), _el$8);
2456
+ _$setProp(_el$8, "onMouseDown", toggleSort);
2457
+ _$insert(_el$8, () => clip(byPressure() ? "Sort: pressure" : "Sort: activity", width()));
2458
+ _$setProp(_el$9, "onMouseDown", toggleGroups);
2459
+ _$insert(_el$9, () => clip(collapsed().size ? "Expand groups" : "Collapse groups", width()));
2068
2460
  _$effect((_p$) => {
2069
- var _v$ = props.width, _v$2 = colors().muted, _v$3 = colors().base, _v$4 = colors().muted, _v$5 = colors().muted, _v$6 = colors().muted, _v$7 = colors().muted, _v$8 = colors().muted, _v$9 = colors().muted;
2070
- _v$ !== _p$.e && (_p$.e = _$setProp(_el$2, "maxWidth", _v$, _p$.e));
2071
- _v$2 !== _p$.t && (_p$.t = _$setProp(_el$2, "borderColor", _v$2, _p$.t));
2072
- _v$3 !== _p$.a && (_p$.a = _$setProp(_el$2, "titleColor", _v$3, _p$.a));
2461
+ var _v$ = props.width, _v$2 = colors().muted, _v$3 = colors().base, _v$4 = colors().muted, _v$5 = colors().muted, _v$6 = colors().muted, _v$7 = colors().muted;
2462
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$4, "maxWidth", _v$, _p$.e));
2463
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$4, "borderColor", _v$2, _p$.t));
2464
+ _v$3 !== _p$.a && (_p$.a = _$setProp(_el$4, "titleColor", _v$3, _p$.a));
2073
2465
  _v$4 !== _p$.o && (_p$.o = _$setProp(_el$5, "fg", _v$4, _p$.o));
2074
- _v$5 !== _p$.i && (_p$.i = _$setProp(_el$8, "fg", _v$5, _p$.i));
2075
- _v$6 !== _p$.n && (_p$.n = _$setProp(_el$9, "fg", _v$6, _p$.n));
2076
- _v$7 !== _p$.s && (_p$.s = _$setProp(_el$0, "fg", _v$7, _p$.s));
2077
- _v$8 !== _p$.h && (_p$.h = _$setProp(_el$11, "fg", _v$8, _p$.h));
2078
- _v$9 !== _p$.r && (_p$.r = _$setProp(_el$12, "fg", _v$9, _p$.r));
2466
+ _v$5 !== _p$.i && (_p$.i = _$setProp(_el$6, "fg", _v$5, _p$.i));
2467
+ _v$6 !== _p$.n && (_p$.n = _$setProp(_el$8, "fg", _v$6, _p$.n));
2468
+ _v$7 !== _p$.s && (_p$.s = _$setProp(_el$9, "fg", _v$7, _p$.s));
2079
2469
  return _p$;
2080
2470
  }, {
2081
2471
  e: undefined,
@@ -2084,11 +2474,9 @@ function CompactSidebar(props) {
2084
2474
  o: undefined,
2085
2475
  i: undefined,
2086
2476
  n: undefined,
2087
- s: undefined,
2088
- h: undefined,
2089
- r: undefined
2477
+ s: undefined
2090
2478
  });
2091
- return _el$2;
2479
+ return _el$4;
2092
2480
  })();
2093
2481
  }
2094
2482
 
@@ -2097,573 +2485,892 @@ function GuardView(props) {
2097
2485
  const consumer = props.state.acquire(props.sessionID);
2098
2486
  createEffect2(() => consumer.update(props.sessionID));
2099
2487
  onCleanup(() => consumer.release());
2100
- const rows = createMemo2(() => props.family ?? props.state.rows([], !props.expanded));
2101
- const current = createMemo2(() => rows().find((row2) => row2.node.sessionId === props.sessionID));
2102
- const trace = createMemo2(() => props.state.contextTrace(props.expanded ? current() : undefined));
2103
- const chart = createMemo2(() => contextChart(trace().samples, current()?.snapshot?.capacityTokens ?? trace().capacity, current()?.snapshot?.policy.triggerTokens, props.width, current()?.snapshot?.measurement ? (current()?.snapshot?.measurement?.at ?? 0) + props.state.age(props.sessionID) : undefined));
2104
- const detail = createMemo2(() => {
2105
- const lines = detailLines(current(), props.width, props.expanded, props.state.age(props.sessionID));
2106
- if (props.expanded) {
2107
- const byId = new Map(rows().map((row2) => [row2.node.sessionId, row2.node]));
2108
- const ancestry = [], seen = new Set;
2109
- let node = byId.get(props.sessionID);
2110
- while (node && !seen.has(node.sessionId)) {
2111
- seen.add(node.sessionId);
2112
- ancestry.unshift(node.agent ?? node.sessionId);
2113
- node = node.parentId ? byId.get(node.parentId) : undefined;
2114
- }
2115
- lines.push(...wrap(`Ancestry: ${ancestry.join(" / ")}`, props.width));
2116
- }
2117
- if (props.inspectId && props.inspectId !== props.sessionID) {
2118
- const inspected = rows().find((row2) => row2.node.sessionId === props.inspectId);
2119
- lines.push(...wrap(`Inspect: ${props.inspectId}`, props.width), ...detailLines(inspected, props.width, true, props.state.age(props.inspectId)));
2120
- }
2121
- return lines;
2122
- });
2123
- const visible = createMemo2(() => {
2124
- if (!props.expanded)
2125
- return rows().slice(0, 7);
2126
- const index = Math.max(0, rows().findIndex((row2) => row2.node.sessionId === props.selectedId));
2127
- return rows().slice(Math.max(0, index - 10), Math.max(0, index - 10) + 25);
2128
- });
2129
- return _$createComponent2(Show2, {
2130
- get when() {
2131
- return props.expanded;
2488
+ const rows = createMemo2(() => props.state.rows([], true));
2489
+ return _$createComponent2(CompactSidebar, {
2490
+ get sessionID() {
2491
+ return props.sessionID;
2132
2492
  },
2133
- get fallback() {
2134
- return _$createComponent2(CompactSidebar, {
2135
- get sessionID() {
2136
- return props.sessionID;
2137
- },
2138
- get state() {
2139
- return props.state;
2140
- },
2141
- get context() {
2142
- return props.context;
2143
- },
2144
- get rows() {
2145
- return rows();
2146
- },
2147
- get width() {
2148
- return props.width;
2149
- }
2150
- });
2493
+ get state() {
2494
+ return props.state;
2151
2495
  },
2152
- get children() {
2153
- var _el$ = _$createElement2("box"), _el$2 = _$createElement2("text"), _el$3 = _$createTextNode2(`Context Guard / `), _el$4 = _$createElement2("text"), _el$5 = _$createElement2("text"), _el$6 = _$createElement2("text"), _el$7 = _$createElement2("text"), _el$8 = _$createTextNode2(`Family (`), _el$9 = _$createTextNode2(`)`), _el$0 = _$createElement2("text");
2154
- _$insertNode2(_el$, _el$2);
2155
- _$insertNode2(_el$, _el$4);
2156
- _$insertNode2(_el$, _el$5);
2157
- _$insertNode2(_el$, _el$6);
2158
- _$insertNode2(_el$, _el$7);
2159
- _$insertNode2(_el$, _el$0);
2160
- _$setProp2(_el$, "flexDirection", "column");
2161
- _$setProp2(_el$, "gap", 0);
2162
- _$insertNode2(_el$2, _el$3);
2163
- _$insert2(_el$2, () => props.state.health()?.mode ?? "?", null);
2164
- _$insert2(_el$4, (() => {
2165
- var _c$ = _$memo2(() => !!["2.0.29", "2.1.2"].includes(props.context.app.version));
2166
- return () => _c$() ? "" : clip(`Runtime ${props.context.app.version}: unverified (tested: 2.0.29, 2.1.2)`, props.width);
2167
- })());
2168
- _$insert2(_el$5, () => clip(`${props.state.connection()} / ${props.state.health()?.enforcement ?? "?"}`, props.width));
2169
- _$insert2(_el$, _$createComponent2(For2, {
2170
- get each() {
2171
- return chart();
2172
- },
2173
- children: (item) => (() => {
2174
- var _el$1 = _$createElement2("text");
2175
- _$insert2(_el$1, _$createComponent2(Show2, {
2176
- get when() {
2177
- return item.segments;
2178
- },
2179
- get fallback() {
2180
- return item.line;
2181
- },
2182
- get children() {
2183
- return _$createComponent2(For2, {
2184
- get each() {
2185
- return item.segments;
2186
- },
2187
- children: (part) => (() => {
2188
- var _el$10 = _$createElement2("span");
2189
- _$insert2(_el$10, () => part.text);
2190
- _$effect2((_$p) => _$setProp2(_el$10, "style", {
2191
- fg: part.tone === "axis" || part.tone === "held" ? props.context.theme.text.muted : part.tone === "threshold" ? props.context.theme.text.feedback.warning.base : current()?.freshness === "fresh" ? props.context.theme.text.base : props.context.theme.text.muted
2192
- }, _$p));
2193
- return _el$10;
2194
- })()
2195
- });
2196
- }
2197
- }));
2198
- _$effect2((_$p) => _$setProp2(_el$1, "fg", item.threshold ? props.context.theme.text.feedback.warning.base : props.context.theme.text.muted, _$p));
2199
- return _el$1;
2200
- })()
2201
- }), _el$6);
2202
- _$insert2(_el$, _$createComponent2(For2, {
2203
- get each() {
2204
- return wrap("Recent context samples \u2192 now \xB7 \u2504 trigger \xB7 muted held", props.width);
2205
- },
2206
- children: (line) => (() => {
2207
- var _el$11 = _$createElement2("text");
2208
- _$insert2(_el$11, line);
2209
- _$effect2((_$p) => _$setProp2(_el$11, "fg", props.context.theme.text.muted, _$p));
2210
- return _el$11;
2211
- })()
2212
- }), _el$6);
2213
- _$insert2(_el$, _$createComponent2(For2, {
2214
- get each() {
2215
- return detail();
2216
- },
2217
- children: (line) => (() => {
2218
- var _el$12 = _$createElement2("text");
2219
- _$insert2(_el$12, line);
2220
- _$effect2((_$p) => _$setProp2(_el$12, "fg", props.context.theme.text.base, _$p));
2221
- return _el$12;
2222
- })()
2223
- }), _el$6);
2224
- _$insert2(_el$, _$createComponent2(For2, {
2225
- get each() {
2226
- return props.state.health()?.reasons ?? [];
2227
- },
2228
- children: (reason) => (() => {
2229
- var _el$13 = _$createElement2("text");
2230
- _$insert2(_el$13, () => clip(reason, props.width));
2231
- _$effect2((_$p) => _$setProp2(_el$13, "fg", props.context.theme.text.feedback.warning.base, _$p));
2232
- return _el$13;
2233
- })()
2234
- }), _el$6);
2235
- _$insert2(_el$6, (() => {
2236
- var _c$2 = _$memo2(() => props.state.health()?.mode === "observe");
2237
- return () => _c$2() ? current()?.pressure === "crossed" ? "Observe: would compact" : "Observe: no admission" : "";
2238
- })());
2239
- _$insertNode2(_el$7, _el$8);
2240
- _$insertNode2(_el$7, _el$9);
2241
- _$insert2(_el$7, () => rows().length, _el$9);
2242
- _$insert2(_el$7, () => props.state.complete() ? "" : " partial", null);
2243
- _$insert2(_el$, _$createComponent2(For2, {
2244
- get each() {
2245
- return visible();
2246
- },
2247
- children: (row2) => (() => {
2248
- var _el$14 = _$createElement2("box");
2249
- _$setProp2(_el$14, "flexDirection", "column");
2250
- _$setProp2(_el$14, "onMouseDown", () => {
2251
- if (row2.hidden)
2252
- props.state.expand(row2.node.sessionId);
2253
- else {
2254
- if (props.expanded)
2255
- props.context.ui.panel.close();
2256
- props.context.ui.router.navigate({
2257
- type: "session",
2258
- sessionID: row2.node.sessionId
2259
- });
2260
- }
2261
- });
2262
- _$insert2(_el$14, _$createComponent2(For2, {
2263
- get each() {
2264
- return rowLines(row2, Math.max(1, props.width - (props.expanded ? 2 : 0))).map((line) => props.expanded ? `${row2.node.sessionId === props.selectedId ? ">" : " "} ${line}` : line);
2265
- },
2266
- children: (line) => (() => {
2267
- var _el$15 = _$createElement2("text");
2268
- _$insert2(_el$15, line);
2269
- _$effect2((_$p) => _$setProp2(_el$15, "fg", row2.pressure === "crossed" ? props.context.theme.text.feedback.error.base : row2.pressure === "near" ? props.context.theme.text.feedback.warning.base : props.context.theme.text.base, _$p));
2270
- return _el$15;
2271
- })()
2272
- }));
2273
- _$effect2((_$p) => _$setProp2(_el$14, "id", `context-guard.row.${row2.node.sessionId}`, _$p));
2274
- return _el$14;
2275
- })()
2276
- }), _el$0);
2277
- _$setProp2(_el$0, "onMouseDown", () => props.context.ui.panel.open("context-guard.monitor"));
2278
- _$insert2(_el$0, (() => {
2279
- var _c$3 = _$memo2(() => rows().length > visible().length);
2280
- return () => _c$3() ? `+ ${rows().length - visible().length} rows; open panel` : "Open: /context-guard";
2281
- })());
2282
- _$effect2((_p$) => {
2283
- var _v$ = props.context.theme.text.base, _v$2 = props.context.theme.text.muted, _v$3 = props.context.theme.text.muted, _v$4 = props.context.theme.text.muted, _v$5 = props.context.theme.text.base, _v$6 = props.context.theme.text.muted;
2284
- _v$ !== _p$.e && (_p$.e = _$setProp2(_el$2, "fg", _v$, _p$.e));
2285
- _v$2 !== _p$.t && (_p$.t = _$setProp2(_el$4, "fg", _v$2, _p$.t));
2286
- _v$3 !== _p$.a && (_p$.a = _$setProp2(_el$5, "fg", _v$3, _p$.a));
2287
- _v$4 !== _p$.o && (_p$.o = _$setProp2(_el$6, "fg", _v$4, _p$.o));
2288
- _v$5 !== _p$.i && (_p$.i = _$setProp2(_el$7, "fg", _v$5, _p$.i));
2289
- _v$6 !== _p$.n && (_p$.n = _$setProp2(_el$0, "fg", _v$6, _p$.n));
2290
- return _p$;
2291
- }, {
2292
- e: undefined,
2293
- t: undefined,
2294
- a: undefined,
2295
- o: undefined,
2296
- i: undefined,
2297
- n: undefined
2298
- });
2299
- return _el$;
2496
+ get context() {
2497
+ return props.context;
2498
+ },
2499
+ get rows() {
2500
+ return rows();
2501
+ },
2502
+ get width() {
2503
+ return props.width;
2300
2504
  }
2301
2505
  });
2302
2506
  }
2303
2507
 
2304
2508
  // src/tui/panel.tsx
2305
- import { effect as _$effect4 } from "@opentui/solid";
2306
- import { memo as _$memo3 } from "@opentui/solid";
2509
+ import { use as _$use } from "@opentui/solid";
2307
2510
  import { insertNode as _$insertNode4 } from "@opentui/solid";
2511
+ import { memo as _$memo2 } from "@opentui/solid";
2512
+ import { effect as _$effect4 } from "@opentui/solid";
2513
+ import { createComponent as _$createComponent5 } from "@opentui/solid";
2308
2514
  import { insert as _$insert4 } from "@opentui/solid";
2309
- import { createComponent as _$createComponent4 } from "@opentui/solid";
2310
- import { use as _$use } from "@opentui/solid";
2311
2515
  import { setProp as _$setProp4 } from "@opentui/solid";
2312
2516
  import { createElement as _$createElement4 } from "@opentui/solid";
2313
- import { createSignal as createSignal4, createMemo as createMemo3, createEffect as createEffect4, onCleanup as onCleanup3, Show as Show3, For as For4 } from "solid-js";
2517
+ import { createSignal as createSignal3, createMemo as createMemo4, createEffect as createEffect3, on, onCleanup as onCleanup2, Show as Show3, For as For4 } from "solid-js";
2314
2518
 
2315
2519
  // src/tui/charts.tsx
2316
2520
  init_chartformat();
2521
+ init_historyclient();
2317
2522
  init_format();
2318
- import { effect as _$effect3 } from "@opentui/solid";
2319
- import { insertNode as _$insertNode3 } from "@opentui/solid";
2320
- import { insert as _$insert3 } from "@opentui/solid";
2523
+ import { effect as _$effect2 } from "@opentui/solid";
2524
+ import { insertNode as _$insertNode2 } from "@opentui/solid";
2525
+ import { insert as _$insert2 } from "@opentui/solid";
2321
2526
  import { createComponent as _$createComponent3 } from "@opentui/solid";
2322
- import { setProp as _$setProp3 } from "@opentui/solid";
2323
- import { createElement as _$createElement3 } from "@opentui/solid";
2324
- import { createEffect as createEffect3, createSignal as createSignal3, onCleanup as onCleanup2, For as For3 } from "solid-js";
2527
+ import { setProp as _$setProp2 } from "@opentui/solid";
2528
+ import { createElement as _$createElement2 } from "@opentui/solid";
2529
+ import { For as For2 } from "solid-js";
2325
2530
  function HistoryCharts(props) {
2326
- const [result, setResult] = createSignal3();
2327
- const [status, setStatus] = createSignal3("Loading retained history...");
2328
- const [range, setRange] = createSignal3({
2329
- from: 0,
2330
- to: 0
2331
- });
2332
- createEffect3(() => {
2333
- const { sessionId: id, windowMs } = props;
2334
- props.refresh;
2335
- let pending = false, disposed = false;
2336
- let controller;
2337
- setResult(undefined);
2338
- async function read() {
2339
- if (pending || disposed)
2340
- return;
2341
- pending = true;
2342
- controller = new AbortController;
2343
- const to = Date.now(), from = Math.max(0, to - windowMs);
2344
- try {
2345
- const next = await props.state.history(id, from, to, AbortSignal.any([controller.signal, AbortSignal.timeout(5000)]));
2346
- if (!disposed) {
2347
- setResult(next);
2348
- setRange({
2349
- from,
2350
- to
2351
- });
2352
- setStatus("");
2353
- }
2354
- } catch {
2355
- if (!disposed)
2356
- setStatus("History unavailable. The owning server must support chart history RPC. Press r to retry.");
2357
- } finally {
2358
- pending = false;
2359
- }
2360
- }
2361
- read();
2362
- const timer = setInterval(() => {
2363
- read();
2364
- }, 5000);
2365
- onCleanup2(() => {
2366
- disposed = true;
2367
- controller?.abort();
2368
- clearInterval(timer);
2369
- });
2370
- });
2371
2531
  const lines = () => {
2372
- const current = result();
2373
- return current ? historyChartLines(current, Math.max(4, props.width), range().from, range().to, props.metricIndex) : [];
2532
+ const current = props.view.result;
2533
+ return current ? historyChartLines(current, Math.max(4, props.width), props.view.from ?? 0, props.view.to ?? 0, props.metricIndex) : [];
2374
2534
  };
2375
2535
  return (() => {
2376
- var _el$ = _$createElement3("box"), _el$2 = _$createElement3("box");
2377
- _$insertNode3(_el$, _el$2);
2378
- _$setProp3(_el$, "flexDirection", "column");
2379
- _$insert3(_el$, _$createComponent3(For3, {
2536
+ var _el$ = _$createElement2("box"), _el$2 = _$createElement2("box");
2537
+ _$insertNode2(_el$, _el$2);
2538
+ _$setProp2(_el$, "flexDirection", "column");
2539
+ _$insert2(_el$, _$createComponent3(For2, {
2380
2540
  get each() {
2381
2541
  return wrap(`Performance charts \xB7 ${props.sessionId}`, props.width);
2382
2542
  },
2383
2543
  children: (line) => (() => {
2384
- var _el$3 = _$createElement3("text");
2385
- _$insert3(_el$3, line);
2386
- _$effect3((_$p) => _$setProp3(_el$3, "fg", props.context.theme.text.base, _$p));
2544
+ var _el$3 = _$createElement2("text");
2545
+ _$insert2(_el$3, line);
2546
+ _$effect2((_$p) => _$setProp2(_el$3, "fg", props.context.theme.text.base, _$p));
2387
2547
  return _el$3;
2388
2548
  })()
2389
2549
  }), _el$2);
2390
- _$insert3(_el$, _$createComponent3(For3, {
2550
+ _$insert2(_el$, _$createComponent3(For2, {
2391
2551
  get each() {
2392
- return wrap(status(), props.width);
2552
+ return wrap(`Window: ${new Map([[300000, "5 minutes"], [3600000, "1 hour"], [86400000, "24 hours"], [604800000, "7 days"]]).get(props.windowMs) ?? `${props.windowMs / 60000} minutes`} \xB7 [ / ] changes`, props.width);
2393
2553
  },
2394
2554
  children: (line) => (() => {
2395
- var _el$4 = _$createElement3("text");
2396
- _$insert3(_el$4, line);
2397
- _$effect3((_$p) => _$setProp3(_el$4, "fg", props.context.theme.text.muted, _$p));
2555
+ var _el$4 = _$createElement2("text");
2556
+ _$insert2(_el$4, line);
2557
+ _$effect2((_$p) => _$setProp2(_el$4, "fg", props.context.theme.text.muted, _$p));
2398
2558
  return _el$4;
2399
2559
  })()
2400
2560
  }), _el$2);
2401
- _$setProp3(_el$2, "flexDirection", "column");
2402
- _$setProp3(_el$2, "paddingTop", 1);
2403
- _$insert3(_el$2, _$createComponent3(For3, {
2561
+ _$insert2(_el$, _$createComponent3(For2, {
2404
2562
  get each() {
2405
- return lines();
2563
+ return historyStatusLines(props.view).flatMap((line) => wrap(line, props.width));
2406
2564
  },
2407
2565
  children: (line) => (() => {
2408
- var _el$5 = _$createElement3("text");
2409
- _$insert3(_el$5, line || " ");
2410
- _$effect3((_$p) => _$setProp3(_el$5, "fg", props.context.theme.text.base, _$p));
2566
+ var _el$5 = _$createElement2("text");
2567
+ _$insert2(_el$5, line);
2568
+ _$effect2((_$p) => _$setProp2(_el$5, "fg", props.context.theme.text.muted, _$p));
2411
2569
  return _el$5;
2412
2570
  })()
2571
+ }), _el$2);
2572
+ _$setProp2(_el$2, "flexDirection", "column");
2573
+ _$setProp2(_el$2, "paddingTop", 1);
2574
+ _$insert2(_el$2, _$createComponent3(For2, {
2575
+ get each() {
2576
+ return lines();
2577
+ },
2578
+ children: (line) => (() => {
2579
+ var _el$6 = _$createElement2("text");
2580
+ _$insert2(_el$6, line || " ");
2581
+ _$effect2((_$p) => _$setProp2(_el$6, "fg", props.context.theme.text.base, _$p));
2582
+ return _el$6;
2583
+ })()
2413
2584
  }));
2414
2585
  return _el$;
2415
2586
  })();
2416
2587
  }
2417
2588
 
2589
+ // src/tui/historystate.ts
2590
+ init_historyclient();
2591
+
2592
+ class HistoryState {
2593
+ options;
2594
+ query;
2595
+ generation = 0;
2596
+ active;
2597
+ pending = false;
2598
+ stopped = false;
2599
+ paused = false;
2600
+ timer;
2601
+ view = { status: "idle", stale: false };
2602
+ now;
2603
+ schedule;
2604
+ cancel;
2605
+ constructor(options) {
2606
+ this.options = options;
2607
+ this.now = options.now ?? Date.now;
2608
+ this.schedule = options.schedule ?? ((callback, ms) => setTimeout(callback, ms));
2609
+ this.cancel = options.cancel ?? clearTimeout;
2610
+ }
2611
+ setQuery(query) {
2612
+ if (this.stopped || query.location === this.query?.location && query.sessionId === this.query.sessionId && query.windowMs === this.query.windowMs)
2613
+ return;
2614
+ this.generation++;
2615
+ this.active?.abort();
2616
+ this.query = { ...query };
2617
+ this.view = { status: "loading", stale: false };
2618
+ if (this.paused)
2619
+ this.publish(this.view);
2620
+ else
2621
+ this.refresh();
2622
+ }
2623
+ pause() {
2624
+ if (this.stopped || this.paused)
2625
+ return;
2626
+ this.paused = true;
2627
+ this.generation++;
2628
+ this.pending = false;
2629
+ this.clearTimer();
2630
+ this.active?.abort();
2631
+ if (this.view.status === "loading" && this.view.result)
2632
+ this.publish({
2633
+ ...this.view,
2634
+ status: "ready",
2635
+ reason: undefined,
2636
+ stale: true
2637
+ });
2638
+ }
2639
+ resume() {
2640
+ if (this.stopped || !this.paused)
2641
+ return;
2642
+ this.paused = false;
2643
+ if (this.query && this.view.status === "loading")
2644
+ this.refresh();
2645
+ else if (this.query && (this.view.status === "ready" || this.view.reason === "timeout" || this.view.reason === "unavailable"))
2646
+ this.timer = this.schedule(() => {
2647
+ this.timer = undefined;
2648
+ this.refresh();
2649
+ }, 5000);
2650
+ }
2651
+ refresh() {
2652
+ if (this.stopped || this.paused || !this.query)
2653
+ return;
2654
+ this.clearTimer();
2655
+ this.pending = true;
2656
+ this.publish({
2657
+ ...this.view,
2658
+ status: "loading",
2659
+ reason: undefined,
2660
+ stale: this.view.result !== undefined
2661
+ });
2662
+ this.pump();
2663
+ }
2664
+ dispose() {
2665
+ this.stopped = true;
2666
+ this.generation++;
2667
+ this.pending = false;
2668
+ this.clearTimer();
2669
+ this.active?.abort();
2670
+ }
2671
+ clearTimer() {
2672
+ if (this.timer !== undefined)
2673
+ this.cancel(this.timer);
2674
+ this.timer = undefined;
2675
+ }
2676
+ publish(view) {
2677
+ const { reason, ...rest } = view;
2678
+ this.view = { ...rest, ...reason === undefined ? {} : { reason } };
2679
+ this.options.changed(this.view);
2680
+ }
2681
+ async pump() {
2682
+ if (this.stopped || this.paused || this.active || !this.pending || !this.query)
2683
+ return;
2684
+ this.pending = false;
2685
+ const query = this.query, generation = this.generation;
2686
+ const controller = new AbortController;
2687
+ this.active = controller;
2688
+ const signal = AbortSignal.any([
2689
+ controller.signal,
2690
+ this.options.deadline?.() ?? AbortSignal.timeout(5000)
2691
+ ]);
2692
+ const to = Math.max(0, Math.floor(this.now())), from = Math.max(0, to - query.windowMs);
2693
+ const current = () => !this.stopped && generation === this.generation;
2694
+ try {
2695
+ const result = checkedHistory(await this.options.read(query.sessionId, from, to, signal), query.sessionId);
2696
+ if (!current())
2697
+ return;
2698
+ if (signal.aborted)
2699
+ throw historyFailure(undefined, signal);
2700
+ this.publish({
2701
+ status: result.enabled ? "ready" : "disabled",
2702
+ result,
2703
+ from,
2704
+ to,
2705
+ loadedAt: this.now(),
2706
+ stale: false
2707
+ });
2708
+ } catch (error) {
2709
+ if (current())
2710
+ this.publish({
2711
+ ...this.view,
2712
+ status: "unavailable",
2713
+ reason: historyFailure(error, signal).reason,
2714
+ stale: this.view.result !== undefined
2715
+ });
2716
+ } finally {
2717
+ this.active = undefined;
2718
+ if (this.pending && !this.paused) {
2719
+ this.publish({
2720
+ ...this.view,
2721
+ status: "loading",
2722
+ reason: undefined,
2723
+ stale: this.view.result !== undefined
2724
+ });
2725
+ this.pump();
2726
+ } else if (current() && !this.paused && (this.view.status === "ready" || this.view.reason === "timeout" || this.view.reason === "unavailable")) {
2727
+ this.timer = this.schedule(() => {
2728
+ this.timer = undefined;
2729
+ this.refresh();
2730
+ }, 5000);
2731
+ }
2732
+ }
2733
+ }
2734
+ }
2735
+
2736
+ // src/tui/panelview.tsx
2737
+ import { createTextNode as _$createTextNode2 } from "@opentui/solid";
2738
+ import { insertNode as _$insertNode3 } from "@opentui/solid";
2739
+ import { effect as _$effect3 } from "@opentui/solid";
2740
+ import { insert as _$insert3 } from "@opentui/solid";
2741
+ import { setProp as _$setProp3 } from "@opentui/solid";
2742
+ import { createElement as _$createElement3 } from "@opentui/solid";
2743
+ import { createComponent as _$createComponent4 } from "@opentui/solid";
2744
+ import { For as For3, Index as Index2, Show as Show2, createMemo as createMemo3 } from "solid-js";
2745
+ init_format();
2746
+ init_statusformat();
2747
+ init_panelformat();
2748
+ function PanelOverview(props) {
2749
+ const visible = createMemo3(() => {
2750
+ const index = Math.max(0, props.rows.findIndex((row2) => row2.node.sessionId === props.selectedId));
2751
+ const start = Math.max(0, index - 10);
2752
+ return props.rows.slice(start, start + 25);
2753
+ });
2754
+ return _$createComponent4(Index2, {
2755
+ get each() {
2756
+ return visible();
2757
+ },
2758
+ children: (row2) => (() => {
2759
+ var _el$ = _$createElement3("box");
2760
+ _$setProp3(_el$, "flexDirection", "column");
2761
+ _$setProp3(_el$, "flexShrink", 0);
2762
+ _$setProp3(_el$, "onMouseDown", () => props.open(row2()));
2763
+ _$insert3(_el$, _$createComponent4(Index2, {
2764
+ get each() {
2765
+ return panelRowLines(row2(), props.width, row2().node.sessionId === props.selectedId);
2766
+ },
2767
+ children: (line) => (() => {
2768
+ var _el$2 = _$createElement3("text");
2769
+ _$insert3(_el$2, line);
2770
+ _$effect3((_$p) => _$setProp3(_el$2, "fg", row2().pressure === "crossed" || row2().node.status === "failed" ? props.context.theme.text.feedback.error.base : row2().pressure === "near" ? props.context.theme.text.feedback.warning.base : props.context.theme.text.base, _$p));
2771
+ return _el$2;
2772
+ })()
2773
+ }));
2774
+ _$effect3((_$p) => _$setProp3(_el$, "id", `context-guard.row.${row2().node.sessionId}`, _$p));
2775
+ return _el$;
2776
+ })()
2777
+ });
2778
+ }
2779
+ function PanelDetails(props) {
2780
+ const chart = createMemo3(() => contextChart(props.trace.samples, props.row?.snapshot?.capacityTokens ?? props.trace.capacity, props.row?.snapshot?.policy.triggerTokens, props.width, props.row?.snapshot?.measurement ? props.row.snapshot.measurement.at + props.age : undefined));
2781
+ const detail = createMemo3(() => {
2782
+ const row2 = props.row;
2783
+ if (!row2)
2784
+ return ["No selected session; return to Overview."];
2785
+ const byId = new Map(props.rows.map((r) => [r.node.sessionId, r.node]));
2786
+ const ancestry = [], seen = new Set;
2787
+ let node = row2.node;
2788
+ while (!seen.has(node.sessionId)) {
2789
+ seen.add(node.sessionId);
2790
+ ancestry.unshift(node.agent ?? node.sessionId);
2791
+ const parent = node.parentId ? byId.get(node.parentId) : undefined;
2792
+ if (!parent)
2793
+ break;
2794
+ node = parent;
2795
+ }
2796
+ return [`Session: ${row2.node.sessionId}${row2.current ? " (current)" : ""}`, ...detailLines(row2, props.width, true, props.age), `Ancestry: ${ancestry.join(" / ")}`, ...props.reasons.flatMap((reason) => [reasonExplanation(reason), `Health reason code: ${reason}`]), `Route family statistics: ${props.routeID}${props.familyComplete ? "" : " \xB7 partial"}`, ...statistics(props.familyRows, props.routeID, props.familyComplete, props.width), "Guard input = prompt + caches. Native Context uses different totals."].flatMap((line) => wrap(line, props.width));
2797
+ });
2798
+ return (() => {
2799
+ var _el$3 = _$createElement3("box"), _el$4 = _$createElement3("text");
2800
+ _$insertNode3(_el$3, _el$4);
2801
+ _$setProp3(_el$3, "flexDirection", "column");
2802
+ _$setProp3(_el$3, "flexShrink", 0);
2803
+ _$insertNode3(_el$4, _$createTextNode2(`Context history`));
2804
+ _$insert3(_el$3, _$createComponent4(For3, {
2805
+ get each() {
2806
+ return chart();
2807
+ },
2808
+ children: (item) => (() => {
2809
+ var _el$6 = _$createElement3("text");
2810
+ _$insert3(_el$6, _$createComponent4(Show2, {
2811
+ get when() {
2812
+ return item.segments;
2813
+ },
2814
+ get fallback() {
2815
+ return item.line;
2816
+ },
2817
+ get children() {
2818
+ return _$createComponent4(For3, {
2819
+ get each() {
2820
+ return item.segments;
2821
+ },
2822
+ children: (part) => (() => {
2823
+ var _el$7 = _$createElement3("span");
2824
+ _$insert3(_el$7, () => part.text);
2825
+ _$effect3((_$p) => _$setProp3(_el$7, "style", {
2826
+ fg: part.tone === "threshold" ? props.context.theme.text.feedback.warning.base : part.tone === "axis" || part.tone === "held" || props.row?.freshness !== "fresh" ? props.context.theme.text.muted : props.context.theme.text.base
2827
+ }, _$p));
2828
+ return _el$7;
2829
+ })()
2830
+ });
2831
+ }
2832
+ }));
2833
+ _$effect3((_$p) => _$setProp3(_el$6, "fg", item.threshold ? props.context.theme.text.feedback.warning.base : props.context.theme.text.muted, _$p));
2834
+ return _el$6;
2835
+ })()
2836
+ }), null);
2837
+ _$insert3(_el$3, _$createComponent4(For3, {
2838
+ get each() {
2839
+ return contextHistoryLegend.flatMap((line) => wrap(line, props.width));
2840
+ },
2841
+ children: (line) => (() => {
2842
+ var _el$8 = _$createElement3("text");
2843
+ _$insert3(_el$8, line);
2844
+ _$effect3((_$p) => _$setProp3(_el$8, "fg", props.context.theme.text.muted, _$p));
2845
+ return _el$8;
2846
+ })()
2847
+ }), null);
2848
+ _$insert3(_el$3, _$createComponent4(For3, {
2849
+ get each() {
2850
+ return detail();
2851
+ },
2852
+ children: (line) => (() => {
2853
+ var _el$9 = _$createElement3("text");
2854
+ _$insert3(_el$9, line);
2855
+ _$effect3((_$p) => _$setProp3(_el$9, "fg", props.context.theme.text.base, _$p));
2856
+ return _el$9;
2857
+ })()
2858
+ }), null);
2859
+ _$effect3((_$p) => _$setProp3(_el$4, "fg", props.context.theme.text.base, _$p));
2860
+ return _el$3;
2861
+ })();
2862
+ }
2863
+
2418
2864
  // src/tui/panel.tsx
2865
+ init_panelformat();
2419
2866
  init_chartformat();
2420
2867
  init_format();
2868
+ init_statusformat();
2869
+
2870
+ // src/tui/panelcontrols.ts
2871
+ init_format();
2872
+ function inventoryMessages(status) {
2873
+ if (status.scanning)
2874
+ return [`Discovering agents \xB7 ${status.discovered} found`];
2875
+ const lines = [];
2876
+ if (status.failed || status.damaged)
2877
+ lines.push("Family discovery incomplete \xB7 refresh to rescan");
2878
+ if (status.pending && status.discovered >= 2048)
2879
+ lines.push("Partial family \xB7 2048-node limit reached");
2880
+ else if (status.canLoadMore)
2881
+ lines.push(status.discovered >= status.limit ? `Partial family \xB7 ${status.limit}-node limit \xB7 load more allows 2048` : "Partial family \xB7 load more available");
2882
+ else if (!status.complete && !lines.length)
2883
+ lines.push("Family discovery incomplete \xB7 refresh to rescan");
2884
+ return lines;
2885
+ }
2886
+ function footerActions(essential, optional, width) {
2887
+ const result = [];
2888
+ let used = 0;
2889
+ for (const action of [...essential, ...optional]) {
2890
+ const size = cells(action.label) + (result.length ? 1 : 0);
2891
+ if (used + size > width)
2892
+ continue;
2893
+ result.push(action);
2894
+ used += size;
2895
+ }
2896
+ return result;
2897
+ }
2898
+
2899
+ // src/tui/panel.tsx
2421
2900
  function GuardPanel(props) {
2422
- const [showCharts, setShowCharts] = createSignal4(false);
2423
- const [metricIndex, setMetricIndex] = createSignal4(0);
2424
- const [viewportWidth, setViewportWidth] = createSignal4();
2425
- const width = () => Math.max(4, Math.min(props.input.width - 4, viewportWidth() ?? Infinity));
2901
+ const consumer = props.state.acquire(props.input.sessionID);
2902
+ createEffect3(on(() => props.input.sessionID, (id) => consumer.update(id)));
2903
+ onCleanup2(() => consumer.release());
2904
+ const [help, setHelp] = createSignal3(false);
2905
+ let sourceScroll = 0;
2906
+ const [view, setView] = createSignal3("overview");
2907
+ const [metricIndex, setMetricIndex] = createSignal3(0);
2908
+ const [viewportWidth, setViewportWidth] = createSignal3();
2909
+ const [height, setHeight] = createSignal3(24);
2910
+ const width = () => Math.max(1, Math.min(props.input.width - 4, viewportWidth() ?? Infinity));
2911
+ const compact = () => height() < 18;
2426
2912
  const windows = [300000, 3600000, 86400000, 7 * 86400000];
2427
- const [windowIndex, setWindowIndex] = createSignal4(1);
2428
- const [chartRefresh, setChartRefresh] = createSignal4(0);
2429
- const [inspectId, setInspectId] = createSignal4();
2430
- const [selected, setSelected] = createSignal4(props.input.sessionID);
2431
- const [order, setOrder] = createSignal4([]);
2432
- const rows = createMemo3(() => props.state.rows(order()));
2913
+ const [windowIndex, setWindowIndex] = createSignal3(1);
2914
+ const [chartRefresh, setChartRefresh] = createSignal3(0);
2915
+ const [chartView, setChartView] = createSignal3({
2916
+ status: "idle",
2917
+ stale: false
2918
+ });
2919
+ const history = new HistoryState({
2920
+ read: (id, from, to, signal) => props.state.history(id, from, to, signal),
2921
+ changed: setChartView
2922
+ });
2923
+ history.pause();
2924
+ onCleanup2(() => history.dispose());
2925
+ const [selected, setSelected] = createSignal3(props.input.sessionID);
2926
+ const [order, setOrder] = createSignal3([]);
2927
+ const rows = createMemo4(() => props.state.rows(order()));
2928
+ const family = createMemo4(() => routeFamily(props.state.rows([], true), props.input.sessionID));
2929
+ const selectedRow = createMemo4(() => rows().find((row2) => row2.node.sessionId === selected()));
2930
+ createEffect3(on(() => {
2931
+ const id = selected();
2932
+ return id ? [id, windows[windowIndex()] ?? 3600000, props.state.historyLocation(id)] : undefined;
2933
+ }, (query) => {
2934
+ if (query)
2935
+ history.setQuery({
2936
+ sessionId: query[0],
2937
+ windowMs: query[1],
2938
+ location: query[2]
2939
+ });
2940
+ }));
2941
+ createEffect3(on(chartRefresh, () => history.refresh(), {
2942
+ defer: true
2943
+ }));
2433
2944
  const offset = () => Math.max(0, rows().findIndex((row2) => row2.node.sessionId === selected()));
2434
- createEffect4(() => {
2945
+ const trace = createMemo4(() => props.state.contextTrace(selectedRow()));
2946
+ createEffect3(() => {
2947
+ const current = rows().find((row2) => row2.node.sessionId === props.input.sessionID);
2948
+ if (current && current.node.sessionId !== selected())
2949
+ props.state.contextTrace(current);
2950
+ });
2951
+ createEffect3(() => {
2435
2952
  if (!props.input.focused)
2436
2953
  setOrder([]);
2437
2954
  });
2438
- createEffect4(() => {
2439
- setSelected(props.input.sessionID);
2955
+ createEffect3(on(() => props.input.sessionID, (id) => {
2956
+ history.pause();
2957
+ setSelected(id);
2440
2958
  setOrder([]);
2959
+ setHelp(false);
2960
+ sourceScroll = 0;
2961
+ setView("overview");
2962
+ }));
2963
+ createEffect3(() => {
2964
+ const family2 = rows();
2965
+ if (!family2.some((row2) => row2.node.sessionId === selected()))
2966
+ setSelected(family2.find((row2) => row2.node.sessionId === props.input.sessionID)?.node.sessionId ?? family2[0]?.node.sessionId);
2441
2967
  });
2442
2968
  let scroll;
2443
2969
  let follow;
2444
- function revealSelection() {
2970
+ function cancelFollow() {
2445
2971
  if (follow)
2446
2972
  props.context.renderer.removeFrameCallback(follow);
2973
+ follow = undefined;
2974
+ }
2975
+ function afterLayout(action) {
2976
+ if (!props.context.renderer)
2977
+ return;
2978
+ cancelFollow();
2447
2979
  let frames = 0;
2448
2980
  follow = async () => {
2449
- scroll?.scrollChildIntoView(`context-guard.row.${selected()}`);
2450
- if (++frames >= 2 && follow) {
2451
- props.context.renderer.removeFrameCallback(follow);
2452
- follow = undefined;
2453
- } else
2981
+ action();
2982
+ if (++frames >= 2)
2983
+ cancelFollow();
2984
+ else
2454
2985
  props.context.renderer.requestRender();
2455
2986
  };
2456
2987
  props.context.renderer.setFrameCallback(follow);
2457
2988
  props.context.renderer.requestRender();
2458
2989
  }
2459
- onCleanup3(() => {
2460
- if (follow)
2461
- props.context.renderer.removeFrameCallback(follow);
2990
+ function revealSelection() {
2991
+ afterLayout(() => scroll?.scrollChildIntoView(`context-guard.row.${selected()}`));
2992
+ }
2993
+ function toggleHelp() {
2994
+ cancelFollow();
2995
+ if (!help()) {
2996
+ if (view() === "charts")
2997
+ history.pause();
2998
+ sourceScroll = scroll?.scrollTop ?? 0;
2999
+ setHelp(true);
3000
+ afterLayout(() => scroll?.scrollTo(0));
3001
+ } else {
3002
+ setHelp(false);
3003
+ if (view() === "charts")
3004
+ history.resume();
3005
+ afterLayout(() => scroll?.scrollTo(sourceScroll));
3006
+ }
3007
+ }
3008
+ onCleanup2(cancelFollow);
3009
+ createEffect3(() => {
3010
+ view();
3011
+ selected();
3012
+ rows();
3013
+ width();
3014
+ height();
3015
+ if (!help() && view() === "overview")
3016
+ revealSelection();
2462
3017
  });
3018
+ function changeView(next) {
3019
+ if (next !== "overview" && !selectedRow())
3020
+ return;
3021
+ cancelFollow();
3022
+ if (view() === "charts" && next !== "charts")
3023
+ history.pause();
3024
+ setView(next);
3025
+ if (next === "charts")
3026
+ history.resume();
3027
+ scroll?.scrollTo(0);
3028
+ if (next === "overview")
3029
+ revealSelection();
3030
+ }
2463
3031
  function move(delta) {
2464
3032
  setOrder(rows().map((row3) => row3.node.sessionId));
2465
3033
  const row2 = rows()[Math.max(0, Math.min(rows().length - 1, offset() + delta))];
2466
- if (row2) {
3034
+ if (row2)
2467
3035
  setSelected(row2.node.sessionId);
2468
- if (!showCharts())
2469
- revealSelection();
2470
- }
3036
+ if (view() === "charts")
3037
+ scroll?.scrollTo(0);
2471
3038
  }
2472
- props.context.keymap.layer(() => ({
2473
- enabled: () => props.input.focused,
2474
- commands: [{
2475
- id: "context-guard.close",
2476
- title: "Close Context Guard",
2477
- bind: "q",
2478
- run: () => props.input.close()
2479
- }, {
2480
- id: "context-guard.chart-previous",
2481
- title: "Previous chart metric",
2482
- bind: "left",
2483
- run: () => {
2484
- if (showCharts())
2485
- setMetricIndex((i) => (i + chartMetrics.length - 1) % chartMetrics.length);
2486
- }
2487
- }, {
2488
- id: "context-guard.chart-next",
2489
- title: "Next chart metric",
2490
- bind: "right",
2491
- run: () => {
2492
- if (showCharts())
2493
- setMetricIndex((i) => (i + 1) % chartMetrics.length);
2494
- }
2495
- }, {
2496
- id: "context-guard.charts",
2497
- title: "Toggle performance charts",
2498
- bind: "c",
2499
- run: () => setShowCharts((v) => !v)
2500
- }, {
2501
- id: "context-guard.chart-shorter",
2502
- title: "Shorter chart window",
2503
- bind: "[",
2504
- run: () => {
2505
- if (showCharts())
2506
- setWindowIndex((i) => Math.max(0, i - 1));
2507
- }
2508
- }, {
2509
- id: "context-guard.chart-longer",
2510
- title: "Longer chart window",
2511
- bind: "]",
2512
- run: () => {
2513
- if (showCharts())
2514
- setWindowIndex((i) => Math.min(windows.length - 1, i + 1));
2515
- }
2516
- }, {
2517
- id: "context-guard.inspect",
2518
- title: "Inspect selected session",
2519
- bind: "i",
2520
- run: () => setInspectId(inspectId() === selected() ? undefined : selected())
2521
- }, {
2522
- id: "context-guard.refresh",
2523
- title: "Refresh Context Guard",
2524
- bind: "r",
2525
- run: () => {
2526
- setOrder([]);
2527
- props.state.refresh();
2528
- setChartRefresh((n) => n + 1);
2529
- }
2530
- }, {
2531
- id: "context-guard.load-more",
2532
- title: "Load more family",
2533
- bind: "l",
2534
- run: () => props.state.loadMore()
2535
- }, {
2536
- id: "context-guard.next",
2537
- bind: "down",
2538
- run: () => move(1)
2539
- }, {
2540
- id: "context-guard.previous",
2541
- bind: "up",
2542
- run: () => move(-1)
2543
- }, {
2544
- id: "context-guard.expand",
2545
- bind: "space",
3039
+ function open(row2) {
3040
+ if (!row2)
3041
+ return;
3042
+ props.input.close();
3043
+ props.context.ui.router.navigate({
3044
+ type: "session",
3045
+ sessionID: row2.node.sessionId
3046
+ });
3047
+ }
3048
+ function page(delta) {
3049
+ if (!help() && view() === "overview")
3050
+ move(delta * Math.max(1, Math.floor((scroll?.viewport.height ?? 1) / (width() >= 72 ? 1 : 3))));
3051
+ else
3052
+ scroll?.scrollBy(delta * Math.max(1, (scroll.viewport.height ?? 1) - 1));
3053
+ }
3054
+ function edge(last) {
3055
+ if (!help() && view() === "overview")
3056
+ move(last ? rows().length : -rows().length);
3057
+ else
3058
+ scroll?.scrollTo(last ? scroll.scrollHeight : 0);
3059
+ }
3060
+ const inventory = () => props.state.inventoryStatus();
3061
+ const expandable = () => {
3062
+ const row2 = selectedRow();
3063
+ return !!row2 && (row2.hidden > 0 || row2.node.status === "completed" && rows().some((r) => r.node.parentId === row2.node.sessionId));
3064
+ };
3065
+ function command(name, title, bind, run, enabled = () => !help()) {
3066
+ return {
3067
+ id: `context-guard.${name}`,
3068
+ title,
3069
+ bind,
3070
+ enabled,
2546
3071
  run: () => {
2547
- const row2 = rows()[offset()];
2548
- if (row2)
2549
- props.state.expand(row2.node.sessionId);
3072
+ if (props.input.focused && enabled())
3073
+ run();
2550
3074
  }
2551
- }, {
2552
- id: "context-guard.open",
2553
- bind: "return",
3075
+ };
3076
+ }
3077
+ const commands = () => [command("close", "Close Context Guard", "q", () => props.input.close(), () => true), command("help", "Toggle native panel help", "?", toggleHelp, () => true), command("help-back", "Return from help", "escape", toggleHelp, help), command("chart-previous", "Previous chart metric", "left", () => setMetricIndex((i) => (i + chartMetrics.length - 1) % chartMetrics.length), () => !help() && view() === "charts"), command("chart-next", "Next chart metric", "right", () => setMetricIndex((i) => (i + 1) % chartMetrics.length), () => !help() && view() === "charts"), command("charts", "Toggle performance charts", "c", () => changeView(view() === "charts" ? "overview" : "charts"), () => !help() && !!selectedRow()), command("chart-shorter", "Shorter chart window", "[", () => setWindowIndex((i) => Math.max(0, i - 1)), () => !help() && view() === "charts"), command("chart-longer", "Longer chart window", "]", () => setWindowIndex((i) => Math.min(windows.length - 1, i + 1)), () => !help() && view() === "charts"), command("inspect", "Toggle selected session details", "i", () => changeView(view() === "details" ? "overview" : "details"), () => !help() && !!selectedRow()), command("refresh", "Refresh inventory, measurements and active charts", "r", () => {
3078
+ setOrder([]);
3079
+ props.state.refresh();
3080
+ setChartRefresh((n) => n + 1);
3081
+ }), command("load-more", "Load more family", "l", () => props.state.loadMore(), () => !help() && view() === "overview" && inventory().canLoadMore), command("next", "Next session or scroll down", "down", () => help() || view() === "details" ? scroll?.scrollBy(1) : move(1), () => true), command("previous", "Previous session or scroll up", "up", () => help() || view() === "details" ? scroll?.scrollBy(-1) : move(-1), () => true), command("page-next", "Next page", "pagedown", () => page(1), () => true), command("page-previous", "Previous page", "pageup", () => page(-1), () => true), command("first", "First row or top", "home", () => edge(false), () => true), command("last", "Last row or bottom", "end", () => edge(true), () => true), command("expand", "Toggle selected branch", "space", () => {
3082
+ const row2 = selectedRow();
3083
+ if (row2)
3084
+ props.state.expand(row2.node.sessionId);
3085
+ }, () => !help() && view() === "overview" && expandable()), command("open", "Open selected session", "return", () => open(selectedRow()), () => !help() && !!selectedRow())];
3086
+ props.context.keymap.layer(() => ({
3087
+ enabled: () => props.input.focused,
3088
+ commands: commands()
3089
+ }));
3090
+ const shortcut = (name) => props.context.keymap.shortcuts(`context-guard.${name}`).join("/");
3091
+ const helpLines = () => ["Native OpenCode panel \u2014 keyboard help", `Return to ${view()}; selected session: ${selectedRow()?.node.agent ?? selected() ?? "none"}`, "> marks selection; * marks the current OpenCode route. Open selected session navigates there and closes the panel.", "Overview: arrows select agents. Details: arrows scroll. Charts: arrows select sessions; Left/Right changes metric and brackets change window.", "PageUp/PageDown moves selection in Overview and scrolls Details/Charts/help. Home/End reaches the first/last row or top/bottom.", "Space toggles a branch in Overview. Load more resumes pending family discovery when available.", ...inventoryMessages(inventory()), `Discovered ${inventory().discovered} \xB7 node limit ${inventory().limit} \xB7 pages ${inventory().pages} \xB7 pending branches ${inventory().pending}`, "Refresh rescans inventory and measurements; it cannot resume suspended enforcement. Reload/repair instructions remain in Guard status.", "Configured native bindings (unbound actions have no shortcut):", ...commands().map((c) => `${props.context.keymap.shortcuts(c.id ?? "").join(" / ") || "Unbound"}: ${c.title}`), "Default bindings differ: native l loads more; standalone l edits location. Native brackets change windows; standalone brackets inspect observations and w changes windows.", "Open Context Guard is also available in the root prompt slash menu and command palette. Set the client plugin option openShortcut for keyboard entry from a child composer.", "End of native panel help"].flatMap((line) => wrap(line, width()));
3092
+ const statusLines = () => monitorStatusLines(props.state.connection(), props.state.health(), rows().some((row2) => row2.snapshot?.measurement !== undefined), width() < 28).flatMap((line) => wrap(line, width()));
3093
+ const notice = () => compactionLabel(selectedRow());
3094
+ const actions = () => {
3095
+ const action = (name, label) => ({
3096
+ id: name,
3097
+ label: ["help", "close"].includes(name) && cells(`${shortcut(name)} ${label}`) > Math.floor((width() - 1) / 2) ? label : `${shortcut(name) ? shortcut(name) + " " : ""}${label}`,
2554
3098
  run: () => {
2555
- const row2 = rows()[offset()];
2556
- if (row2) {
2557
- props.input.close();
2558
- props.context.ui.router.navigate({
2559
- type: "session",
2560
- sessionID: row2.node.sessionId
2561
- });
2562
- }
3099
+ commands().find((c) => c.id === `context-guard.${name}`)?.run();
2563
3100
  }
2564
- }]
2565
- }));
2566
- const hints = () => showCharts() ? ["c back \xB7 q close", "\u2190 / \u2192 metric \xB7 \u2191 / \u2193 session", `[ / ] window \xB7 ${["5 minutes", "1 hour", "24 hours", "7 days"][windowIndex()]}`, "r refresh \xB7 Enter open session"] : ["c charts \xB7 q close", `\u2191 / \u2193 select ${offset() + 1} \xB7 Enter open`, "space expand \xB7 i inspect", "r refresh \xB7 l load more"];
3101
+ });
3102
+ const optional = help() ? [action("help-back", "back")] : [...view() === "overview" && inventory().canLoadMore ? [action("load-more", "more")] : [], ...view() === "charts" ? [action("charts", "back")] : [], ...selectedRow() ? [action("inspect", view() === "details" ? "back" : "details")] : [], ...view() !== "charts" && selectedRow() ? [action("charts", "charts")] : [], ...view() === "overview" && expandable() ? [action("expand", "branch")] : []];
3103
+ return footerActions([action("help", help() ? "back" : "help"), action("close", "close")], optional, width());
3104
+ };
3105
+ const hint = () => {
3106
+ if (help())
3107
+ return `${shortcut("previous")}/${shortcut("next")} scroll`;
3108
+ if (view() === "charts")
3109
+ return `${shortcut("refresh")} refresh \xB7 ${shortcut("chart-previous")}/${shortcut("chart-next")} metric ${shortcut("previous")}/${shortcut("next")} session ${shortcut("chart-shorter")}/${shortcut("chart-longer")} window`;
3110
+ if (view() === "details")
3111
+ return `${shortcut("previous")}/${shortcut("next")} scroll \xB7 ${shortcut("page-previous")}/${shortcut("page-next")} page`;
3112
+ return `${shortcut("previous")}/${shortcut("next")} select \xB7 ${shortcut("open")} open \xB7 ${inventory().canLoadMore ? shortcut("load-more") + " more" : shortcut("refresh") + " refresh"}`;
3113
+ };
2567
3114
  return (() => {
2568
- var _el$ = _$createElement4("box"), _el$2 = _$createElement4("scrollbox"), _el$3 = _$createElement4("box"), _el$4 = _$createElement4("text"), _el$5 = _$createElement4("box");
3115
+ var _el$ = _$createElement4("box"), _el$2 = _$createElement4("box"), _el$3 = _$createElement4("text"), _el$6 = _$createElement4("scrollbox"), _el$7 = _$createElement4("box"), _el$8 = _$createElement4("box"), _el$9 = _$createElement4("text");
2569
3116
  _$insertNode4(_el$, _el$2);
2570
- _$insertNode4(_el$, _el$5);
3117
+ _$insertNode4(_el$, _el$6);
3118
+ _$insertNode4(_el$, _el$7);
2571
3119
  _$setProp4(_el$, "flexDirection", "column");
2572
3120
  _$setProp4(_el$, "flexGrow", 1);
3121
+ _$setProp4(_el$, "minHeight", 0);
2573
3122
  _$setProp4(_el$, "paddingLeft", 2);
2574
3123
  _$setProp4(_el$, "paddingRight", 2);
2575
- _$setProp4(_el$, "paddingTop", 1);
2576
- _$setProp4(_el$, "paddingBottom", 1);
3124
+ _$setProp4(_el$, "onSizeChange", function() {
3125
+ setHeight(this.height);
3126
+ });
2577
3127
  _$insertNode4(_el$2, _el$3);
3128
+ _$setProp4(_el$2, "flexDirection", "column");
3129
+ _$setProp4(_el$2, "flexShrink", 0);
3130
+ _$insert4(_el$3, () => clip(width() < 28 ? `${connectionLabel(props.state.connection(), true)} \xB7 Guard` : `${connectionLabel(props.state.connection())} \xB7 Context Guard \xB7 ${help() ? "help" : view()}`, width()));
3131
+ _$insert4(_el$2, _$createComponent5(For4, {
3132
+ get each() {
3133
+ return statusLines();
3134
+ },
3135
+ children: (line) => (() => {
3136
+ var _el$0 = _$createElement4("text");
3137
+ _$insert4(_el$0, line);
3138
+ _$effect4((_$p) => _$setProp4(_el$0, "fg", props.state.health()?.enforcement === "suspended" || props.state.health()?.enforcement === "disabled" ? props.context.theme.text.feedback.warning.base : props.context.theme.text.muted, _$p));
3139
+ return _el$0;
3140
+ })()
3141
+ }), null);
3142
+ _$insert4(_el$2, _$createComponent5(Show3, {
3143
+ get when() {
3144
+ return !["2.0.29", "2.1.2"].includes(props.context.app.version);
3145
+ },
3146
+ get children() {
3147
+ var _el$4 = _$createElement4("text");
3148
+ _$insert4(_el$4, () => clip(`Runtime ${props.context.app.version}: unverified`, width()));
3149
+ _$effect4((_$p) => _$setProp4(_el$4, "fg", props.context.theme.text.feedback.warning.base, _$p));
3150
+ return _el$4;
3151
+ }
3152
+ }), null);
3153
+ _$insert4(_el$2, _$createComponent5(Show3, {
3154
+ get when() {
3155
+ return notice();
3156
+ },
3157
+ children: (message) => (() => {
3158
+ var _el$1 = _$createElement4("text");
3159
+ _$insert4(_el$1, () => clip(message(), width()));
3160
+ _$effect4((_$p) => _$setProp4(_el$1, "fg", props.context.theme.text.feedback.warning.base, _$p));
3161
+ return _el$1;
3162
+ })()
3163
+ }), null);
3164
+ _$insert4(_el$2, _$createComponent5(Show3, {
3165
+ get when() {
3166
+ return _$memo2(() => !!(help() || view() !== "overview"))() && selectedRow();
3167
+ },
3168
+ children: (row2) => (() => {
3169
+ var _el$10 = _$createElement4("text");
3170
+ _$insert4(_el$10, () => clip(`Selected: ${row2().node.agent ?? row2().node.sessionId}`, width()));
3171
+ _$effect4((_$p) => _$setProp4(_el$10, "fg", props.context.theme.text.base, _$p));
3172
+ return _el$10;
3173
+ })()
3174
+ }), null);
3175
+ _$insert4(_el$2, _$createComponent5(Show3, {
3176
+ get when() {
3177
+ return _$memo2(() => !!!help())() && view() === "overview";
3178
+ },
3179
+ get children() {
3180
+ return [_$createComponent5(For4, {
3181
+ get each() {
3182
+ return panelSummary(selectedRow(), width(), props.state.age(selected() ?? ""), compact());
3183
+ },
3184
+ children: (line) => (() => {
3185
+ var _el$11 = _$createElement4("text");
3186
+ _$insert4(_el$11, line);
3187
+ _$effect4((_$p) => _$setProp4(_el$11, "fg", props.context.theme.text.base, _$p));
3188
+ return _el$11;
3189
+ })()
3190
+ }), (() => {
3191
+ var _el$5 = _$createElement4("text");
3192
+ _$insert4(_el$5, () => panelRowHeading(`Family ${rows().length ? offset() + 1 : 0}/${rows().length}${props.state.complete() ? "" : " \xB7 partial"} \xB7 * current`, width()));
3193
+ _$effect4((_$p) => _$setProp4(_el$5, "fg", props.context.theme.text.muted, _$p));
3194
+ return _el$5;
3195
+ })()];
3196
+ }
3197
+ }), null);
3198
+ _$insert4(_el$2, _$createComponent5(Show3, {
3199
+ get when() {
3200
+ return _$memo2(() => !!!help())() && view() === "overview";
3201
+ },
3202
+ get children() {
3203
+ return _$createComponent5(For4, {
3204
+ get each() {
3205
+ return inventoryMessages(inventory());
3206
+ },
3207
+ children: (line) => (() => {
3208
+ var _el$12 = _$createElement4("text");
3209
+ _$insert4(_el$12, () => clip(line, width()));
3210
+ _$effect4((_$p) => _$setProp4(_el$12, "fg", props.context.theme.text.muted, _$p));
3211
+ return _el$12;
3212
+ })()
3213
+ });
3214
+ }
3215
+ }), null);
2578
3216
  _$use((value) => {
2579
3217
  scroll = value;
2580
- }, _el$2);
2581
- _$setProp4(_el$2, "viewportOptions", {
3218
+ }, _el$6);
3219
+ _$setProp4(_el$6, "viewportOptions", {
2582
3220
  onSizeChange: function() {
2583
3221
  setViewportWidth(this.width);
2584
3222
  }
2585
3223
  });
2586
- _$setProp4(_el$2, "flexGrow", 1);
2587
- _$setProp4(_el$2, "minHeight", 0);
2588
- _$insertNode4(_el$3, _el$4);
2589
- _$setProp4(_el$3, "flexDirection", "column");
2590
- _$insert4(_el$3, _$createComponent4(GuardView, {
2591
- get sessionID() {
2592
- return props.input.sessionID;
2593
- },
2594
- get state() {
2595
- return props.state;
2596
- },
2597
- get context() {
2598
- return props.context;
2599
- },
2600
- get width() {
2601
- return width();
2602
- },
2603
- expanded: true,
2604
- get family() {
2605
- return rows();
2606
- },
2607
- get selectedId() {
2608
- return selected();
3224
+ _$setProp4(_el$6, "flexGrow", 1);
3225
+ _$setProp4(_el$6, "minHeight", 0);
3226
+ _$setProp4(_el$6, "scrollX", false);
3227
+ _$insert4(_el$6, _$createComponent5(Show3, {
3228
+ get when() {
3229
+ return help();
2609
3230
  },
2610
- get inspectId() {
2611
- return inspectId();
3231
+ get children() {
3232
+ return _$createComponent5(For4, {
3233
+ get each() {
3234
+ return helpLines();
3235
+ },
3236
+ children: (line) => (() => {
3237
+ var _el$13 = _$createElement4("text");
3238
+ _$insert4(_el$13, line);
3239
+ _$effect4((_$p) => _$setProp4(_el$13, "fg", props.context.theme.text.base, _$p));
3240
+ return _el$13;
3241
+ })()
3242
+ });
2612
3243
  }
2613
- }), _el$4);
2614
- _$insert4(_el$4, () => wrap("Guard input = prompt + caches. Native Context uses different totals.", width()).join("\n"));
2615
- _$insert4(_el$2, _$createComponent4(Show3, {
3244
+ }), null);
3245
+ _$insert4(_el$6, _$createComponent5(Show3, {
2616
3246
  get when() {
2617
- return showCharts();
3247
+ return _$memo2(() => !!!help())() && view() === "overview";
2618
3248
  },
2619
3249
  get children() {
2620
- return _$createComponent4(HistoryCharts, {
2621
- get sessionId() {
3250
+ return _$createComponent5(PanelOverview, {
3251
+ get rows() {
3252
+ return rows();
3253
+ },
3254
+ get selectedId() {
2622
3255
  return selected();
2623
3256
  },
2624
- get windowMs() {
2625
- return windows[windowIndex()] ?? 3600000;
3257
+ get width() {
3258
+ return width();
3259
+ },
3260
+ get context() {
3261
+ return props.context;
3262
+ },
3263
+ open: (row2) => {
3264
+ if (row2.hidden)
3265
+ props.state.expand(row2.node.sessionId);
3266
+ else
3267
+ open(row2);
3268
+ }
3269
+ });
3270
+ }
3271
+ }), null);
3272
+ _$insert4(_el$6, _$createComponent5(Show3, {
3273
+ get when() {
3274
+ return _$memo2(() => !!!help())() && view() === "details";
3275
+ },
3276
+ get children() {
3277
+ return _$createComponent5(PanelDetails, {
3278
+ get row() {
3279
+ return selectedRow();
3280
+ },
3281
+ get rows() {
3282
+ return rows();
3283
+ },
3284
+ get routeID() {
3285
+ return props.input.sessionID;
2626
3286
  },
2627
- get metricIndex() {
2628
- return metricIndex();
3287
+ get familyRows() {
3288
+ return family().rows;
2629
3289
  },
2630
- get refresh() {
2631
- return chartRefresh();
3290
+ get familyComplete() {
3291
+ return _$memo2(() => !!family().complete)() && props.state.complete();
3292
+ },
3293
+ get trace() {
3294
+ return trace();
2632
3295
  },
2633
3296
  get width() {
2634
3297
  return width();
2635
3298
  },
2636
- get state() {
2637
- return props.state;
3299
+ get age() {
3300
+ return props.state.age(selected() ?? "");
2638
3301
  },
2639
3302
  get context() {
2640
3303
  return props.context;
3304
+ },
3305
+ get reasons() {
3306
+ return props.state.health()?.reasons ?? [];
2641
3307
  }
2642
3308
  });
2643
3309
  }
2644
3310
  }), null);
2645
- _$setProp4(_el$5, "flexDirection", "column");
2646
- _$setProp4(_el$5, "flexShrink", 0);
2647
- _$setProp4(_el$5, "paddingTop", 1);
2648
- _$insert4(_el$5, _$createComponent4(For4, {
3311
+ _$insert4(_el$6, _$createComponent5(Show3, {
3312
+ get when() {
3313
+ return _$memo2(() => !!(!help() && view() === "charts"))() && selected();
3314
+ },
3315
+ children: (id) => _$createComponent5(HistoryCharts, {
3316
+ get sessionId() {
3317
+ return id();
3318
+ },
3319
+ get windowMs() {
3320
+ return windows[windowIndex()] ?? 3600000;
3321
+ },
3322
+ get metricIndex() {
3323
+ return metricIndex();
3324
+ },
3325
+ get width() {
3326
+ return width();
3327
+ },
3328
+ get view() {
3329
+ return chartView();
3330
+ },
3331
+ get context() {
3332
+ return props.context;
3333
+ }
3334
+ })
3335
+ }), null);
3336
+ _$insertNode4(_el$7, _el$8);
3337
+ _$insertNode4(_el$7, _el$9);
3338
+ _$setProp4(_el$7, "flexDirection", "column");
3339
+ _$setProp4(_el$7, "flexShrink", 0);
3340
+ _$setProp4(_el$8, "flexDirection", "row");
3341
+ _$setProp4(_el$8, "flexShrink", 0);
3342
+ _$insert4(_el$8, _$createComponent5(For4, {
2649
3343
  get each() {
2650
- return hints().flatMap((hint) => wrap(hint, width()));
3344
+ return actions();
2651
3345
  },
2652
- children: (line) => (() => {
2653
- var _el$6 = _$createElement4("text");
2654
- _$insert4(_el$6, line);
2655
- _$effect4((_$p) => _$setProp4(_el$6, "fg", props.context.theme.text.muted, _$p));
2656
- return _el$6;
3346
+ children: (action, index) => (() => {
3347
+ var _el$14 = _$createElement4("text");
3348
+ _$insert4(_el$14, () => `${index() ? " " : ""}${action.label}`);
3349
+ _$effect4((_p$) => {
3350
+ var _v$5 = props.context.theme.text.muted, _v$6 = action.run;
3351
+ _v$5 !== _p$.e && (_p$.e = _$setProp4(_el$14, "fg", _v$5, _p$.e));
3352
+ _v$6 !== _p$.t && (_p$.t = _$setProp4(_el$14, "onMouseDown", _v$6, _p$.t));
3353
+ return _p$;
3354
+ }, {
3355
+ e: undefined,
3356
+ t: undefined
3357
+ });
3358
+ return _el$14;
2657
3359
  })()
2658
3360
  }));
3361
+ _$insert4(_el$9, () => clip(hint(), width()));
2659
3362
  _$effect4((_p$) => {
2660
- var _v$ = !showCharts(), _v$2 = props.context.theme.text.muted;
2661
- _v$ !== _p$.e && (_p$.e = _$setProp4(_el$3, "visible", _v$, _p$.e));
2662
- _v$2 !== _p$.t && (_p$.t = _$setProp4(_el$4, "fg", _v$2, _p$.t));
3363
+ var _v$ = compact() ? 0 : 1, _v$2 = compact() ? 0 : 1, _v$3 = props.context.theme.text.base, _v$4 = props.context.theme.text.muted;
3364
+ _v$ !== _p$.e && (_p$.e = _$setProp4(_el$, "paddingTop", _v$, _p$.e));
3365
+ _v$2 !== _p$.t && (_p$.t = _$setProp4(_el$, "paddingBottom", _v$2, _p$.t));
3366
+ _v$3 !== _p$.a && (_p$.a = _$setProp4(_el$3, "fg", _v$3, _p$.a));
3367
+ _v$4 !== _p$.o && (_p$.o = _$setProp4(_el$9, "fg", _v$4, _p$.o));
2663
3368
  return _p$;
2664
3369
  }, {
2665
3370
  e: undefined,
2666
- t: undefined
3371
+ t: undefined,
3372
+ a: undefined,
3373
+ o: undefined
2667
3374
  });
2668
3375
  return _el$;
2669
3376
  })();
@@ -2673,14 +3380,18 @@ function GuardPanel(props) {
2673
3380
  var tui_default = Plugin.define({
2674
3381
  id: "context-guard",
2675
3382
  setup(context) {
2676
- if (typeof context.client?.rpc !== "function" || typeof context.ui?.slot !== "function" || typeof context.ui?.panel?.open !== "function" || typeof context.keymap?.layer !== "function") {
3383
+ const openShortcut = context.options.openShortcut;
3384
+ if (openShortcut !== undefined && (typeof openShortcut !== "string" || !openShortcut.trim() || openShortcut.length > 128 || /[\x00-\x1f\x7f-\x9f]/.test(openShortcut))) {
3385
+ throw new Error("Context Guard openShortcut must be a nonempty key sequence of at most 128 characters.");
3386
+ }
3387
+ if (typeof context.client?.rpc !== "function" || typeof context.ui?.slot !== "function" || typeof context.ui?.panel?.open !== "function" || typeof context.keymap?.layer !== "function" || typeof context.keymap?.shortcuts !== "function") {
2677
3388
  throw new Error("Context Guard TUI requires OpenCode 2.0.29 public RPC and panel APIs; use the Guard inspect CLI on unsupported hosts.");
2678
3389
  }
2679
3390
  const state = createGuardState(context), unregister = [];
2680
3391
  try {
2681
3392
  unregister.push(context.ui.slot({
2682
3393
  append: "sidebar.content",
2683
- render: (input) => _$createComponent5(GuardView, {
3394
+ render: (input) => _$createComponent6(GuardView, {
2684
3395
  get sessionID() {
2685
3396
  return input.sessionID;
2686
3397
  },
@@ -2691,12 +3402,12 @@ var tui_default = Plugin.define({
2691
3402
  }));
2692
3403
  unregister.push(context.ui.slot({
2693
3404
  append: "session.panel",
2694
- render: (input) => _$createComponent5(Show4, {
3405
+ render: (input) => _$createComponent6(Show4, {
2695
3406
  get when() {
2696
3407
  return input.name === "context-guard.monitor";
2697
3408
  },
2698
3409
  get children() {
2699
- return _$createComponent5(GuardPanel, {
3410
+ return _$createComponent6(GuardPanel, {
2700
3411
  input,
2701
3412
  state,
2702
3413
  context
@@ -2706,14 +3417,14 @@ var tui_default = Plugin.define({
2706
3417
  }));
2707
3418
  unregister.push(context.ui.slot({
2708
3419
  append: "session.composer.top",
2709
- render: (input) => _$createComponent5(Show4, {
3420
+ render: (input) => _$createComponent6(Show4, {
2710
3421
  get when() {
2711
3422
  return context.data.session.get(input.sessionID)?.parentID;
2712
3423
  },
2713
3424
  get children() {
2714
3425
  var _el$ = _$createElement5("text");
2715
- _$insertNode5(_el$, _$createTextNode3(`Context Guard: open panel`));
2716
3426
  _$setProp5(_el$, "onMouseDown", () => context.ui.panel.open("context-guard.monitor"));
3427
+ _$insert5(_el$, () => `Context Guard: open panel${context.keymap.shortcuts("context-guard.show").length ? ` (${context.keymap.shortcuts("context-guard.show").join(" / ")})` : ""}`);
2717
3428
  _$effect5((_$p) => _$setProp5(_el$, "fg", context.theme.text.muted, _$p));
2718
3429
  return _el$;
2719
3430
  }
@@ -2726,7 +3437,9 @@ var tui_default = Plugin.define({
2726
3437
  title: "Open Context Guard",
2727
3438
  group: "Context Guard",
2728
3439
  palette: true,
2729
- bind: false,
3440
+ ...typeof openShortcut === "string" ? {
3441
+ bind: openShortcut
3442
+ } : {},
2730
3443
  slash: {
2731
3444
  name: "context-guard"
2732
3445
  },