@tangle-network/sandbox-ui 3.0.2 → 3.0.3

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.
Files changed (28) hide show
  1. package/dist/AgentTree-D4d06rfH.js +293 -0
  2. package/dist/{ResearchReport-vqefUbVp.js → ResearchReport-D1TFmldm.js} +2 -1
  3. package/dist/{VirtualList-CMynqkkh.js → VirtualList-DcSGbEtA.js} +28 -1
  4. package/dist/{index-gXcuwPOX.d.ts → index-Bv2iiZaN.d.ts} +135 -2
  5. package/dist/{index-YU_b9xCK.d.ts → index-CHA38az4.d.ts} +3 -61
  6. package/dist/index.d.ts +3 -3
  7. package/dist/index.js +9 -8
  8. package/dist/record/ResearchReport.js +1 -1
  9. package/dist/{record-B_4XWfUz.js → record-C-EUpK-n.js} +3 -2
  10. package/dist/record.js +1 -1
  11. package/dist/report-viewer.js +80 -80
  12. package/dist/run/live.js +2 -2
  13. package/dist/run/record.d.ts +2 -2
  14. package/dist/run/record.js +5 -4
  15. package/dist/run.d.ts +3 -3
  16. package/dist/run.js +6 -5
  17. package/dist/runtime-viewer/{AgentGraphCanvas-DGq7_u8--WPFPgd72.js → AgentGraphCanvas-DGq7_u8--C6yDfIUT.js} +67 -67
  18. package/dist/runtime-viewer/{latest-events-CRq9sGkK-YFXUh9qO.js → latest-events-CRq9sGkK-CfoBl1dP.js} +215 -209
  19. package/dist/{trace-workspace-BiJbv9WQ.js → trace-workspace-B634-EcA.js} +287 -201
  20. package/dist/{tree-data-9D309DqD.js → tree-data-rg5voiY8.js} +1 -1
  21. package/dist/{AgentTree-Cju8jn51.js → use-follow-tail-DZFn7QDO.js} +144 -385
  22. package/dist/vault-tree.js +1 -1
  23. package/dist/{workspace-BrS_rHWN.js → workspace-CJD7oOEa.js} +1 -1
  24. package/dist/{workspace-files-pane-Di_rn4FL.js → workspace-files-pane-D24G7H_Z.js} +1 -1
  25. package/dist/workspace-viewer.js +9970 -9624
  26. package/dist/workspace.js +2 -2
  27. package/package.json +5 -4
  28. package/dist/use-follow-tail-Ht7fZGQQ.js +0 -1
@@ -1,18 +1,16 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D8jQpDG7.js";
2
- import { t as VirtualList } from "./VirtualList-CMynqkkh.js";
3
- import { t as useFollowTail } from "./use-follow-tail-Ht7fZGQQ.js";
4
2
  import { HoverCard, HoverCardContent, HoverCardTrigger } from "@tangle-network/ui/primitives";
5
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
- import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
7
4
  import { Brain, Settings, Timer } from "lucide-react";
8
5
  import { getToolCategory, getToolDisplayMetadata } from "@tangle-network/ui/utils";
9
- import { AgentTimelineColumn, AgentTimelineStep, agentTimelineStepKind } from "@tangle-network/ui/chat/agent-timeline";
10
6
  import { formatCompact, formatCompact as fmt, formatCost, formatSpanDuration, formatSpanDuration as interval, formatUtcTime as utcTime, reportedCount, sumReportedCounts } from "@tangle-network/charts";
7
+ import { AgentTimelineStep, agentTimelineStepKind } from "@tangle-network/ui/chat/agent-timeline";
11
8
  import { SpeakerHeader } from "@tangle-network/ui/chat/speaker";
12
9
  import { ExpandedToolDetail } from "@tangle-network/ui/run/expanded-tool-detail";
13
10
  import { InlineThinkingItem } from "@tangle-network/ui/run/inline-thinking-item";
14
11
  import { RunRowShell } from "@tangle-network/ui/run/run-row-shell";
15
- import { RunStatusIcon, isOpen, runStatus, statusMeta } from "@tangle-network/ui/run/status-meta";
12
+ import { isOpen, runStatus } from "@tangle-network/ui/run/status-meta";
13
+ import { useFollowTail } from "@tangle-network/ui/hooks/use-follow-tail";
16
14
  // src/run/record/model.ts
17
15
  var model_exports = /* @__PURE__ */ __exportAll({
18
16
  agentState: () => agentState,
@@ -639,13 +637,13 @@ function collapsePolls(items, { callsOf, answered, keyOf, min = 2 }) {
639
637
  events
640
638
  }), min);
641
639
  }
640
+ const idleReply = (text) => text.trim().length <= 280 && /\b(waiting|ending the turn|end turn|no new updates|nothing new)\b/i.test(text) ? text.trim() : null;
642
641
  /** Exact repeated idle replies fold without hiding changed statements, tools, errors, prompts or source evidence. */
643
642
  function collapseIdleTurns(rows, callsOf) {
644
643
  return foldRows(rows, (event) => {
645
644
  const d = event.detail;
646
645
  if (d.role !== "assistant" || event.category === "lifecycle" || d.lifecycle || d.reasoning || d.recordedClaim || d.responseStatus === "error" || d.isError || d.contentOmitted || d.publicationNote || d.clip || thinkingMarkers(d).length || callsOf(event).length) return null;
647
- const text = typeof d.publicText === "string" ? d.publicText.trim() : "";
648
- return text.length <= 280 && /\b(waiting|ending the turn|end turn|no new updates|nothing new)\b/i.test(text) ? text : null;
646
+ return idleReply(typeof d.publicText === "string" ? d.publicText : "");
649
647
  }, (a, b) => a === b, (events) => ({
650
648
  kind: "idle",
651
649
  key: `idle:${anchorOf(events[0])}`,
@@ -714,6 +712,10 @@ function promptText(text) {
714
712
  /** A run-lifecycle event (start, stop, restart), drawn as a thin marker row. */
715
713
  const isLifecycle = (event) => event.category === "lifecycle" || event.detail.lifecycle !== void 0;
716
714
  function ToolStep({ item, previous }) {
715
+ if (!item.part) return /* @__PURE__ */ jsx(AgentTimelineStep, {
716
+ item,
717
+ previous
718
+ });
717
719
  const part = item.part, cost = part.state.metadata?.costUsd, time = part.state.time, category = getToolCategory(part.tool);
718
720
  const tone = part.state.status === "error" ? "var(--surface-danger-text)" : [
719
721
  "command",
@@ -827,11 +829,10 @@ const failureText = (text) => {
827
829
  return compact(body.split("\n").find((line) => line.trim()) ?? "").slice(0, 200);
828
830
  };
829
831
  /** One tool call as the kit's row: its verb and subject, collapsed; the input and output when opened; failures inline. */
830
- function toolItems(id, call, result, state, durationMs, live, opener) {
831
- const name = toolName(call.name);
832
- const output = result ? textOf(result) || result.detail.contentOmitted || "" : void 0;
833
- const status = state === "error" ? "error" : state === "pending" || state === "missing" && live ? "running" : "completed";
832
+ function toolItem(id, call, output, state, durationMs, live, cost) {
833
+ const name = toolName(call.name), status = state === "error" ? "error" : state === "pending" || state === "missing" && live ? "running" : "completed";
834
834
  const note = state === "ambiguous" || state === "missing" && !live ? callStatus(state, live) : void 0;
835
+ const duration = durationMs !== null && durationMs >= 0 ? durationMs : void 0;
835
836
  const part = {
836
837
  type: "tool",
837
838
  id,
@@ -841,20 +842,19 @@ function toolItems(id, call, result, state, durationMs, live, opener) {
841
842
  input: inputOf(call.input),
842
843
  ...output !== void 0 ? { output } : {},
843
844
  ...status === "error" ? { error: failureText(output ?? "") || "The call failed." } : {},
844
- ...durationMs !== null && durationMs >= 0 ? { time: {
845
+ ...duration !== void 0 ? { time: {
845
846
  start: 0,
846
- end: durationMs
847
+ end: duration
847
848
  } } : {},
848
849
  metadata: {
849
850
  ...note ? { note } : {},
850
- ...typeof result?.detail.costListUsd === "number" ? { costUsd: result.detail.costListUsd } : {}
851
+ ...cost !== void 0 ? { costUsd: cost } : {}
851
852
  }
852
853
  }
853
854
  };
854
- const known = KIT_TOOLS.has(name.toLowerCase());
855
- const meta = getToolDisplayMetadata(part);
855
+ const known = KIT_TOOLS.has(name.toLowerCase()), meta = getToolDisplayMetadata(part);
856
856
  const detail = argsLabel(call.input) ?? (meta.description || (known ? void 0 : preview(call)));
857
- const item = {
857
+ return {
858
858
  id,
859
859
  kind: "tool",
860
860
  part,
@@ -864,9 +864,12 @@ function toolItems(id, call, result, state, durationMs, live, opener) {
864
864
  label: known ? meta.title : name.replaceAll("_", " "),
865
865
  status: status === "error" ? "error" : status === "running" ? "running" : "success",
866
866
  ...detail ? { detail } : {},
867
- ...durationMs !== null && durationMs >= 0 ? { duration: durationMs } : {}
867
+ ...duration !== void 0 ? { duration } : {}
868
868
  }
869
869
  };
870
+ }
871
+ function toolItems(id, call, result, state, durationMs, live, opener) {
872
+ const item = toolItem(id, call, result ? textOf(result) || result.detail.contentOmitted || "" : void 0, state, durationMs, live, typeof result?.detail.costListUsd === "number" ? result.detail.costListUsd : void 0);
870
873
  const clip = clipOf(result?.detail.clip);
871
874
  return result && clip ? [item, {
872
875
  id: `${id}:clip`,
@@ -877,6 +880,86 @@ function toolItems(id, call, result, state, durationMs, live, opener) {
877
880
  })
878
881
  }] : [item];
879
882
  }
883
+ /** The same prompt control for retained and streamed turns; opening state belongs to the reader. */
884
+ function PromptMore({ text, id, opened, setOpen }) {
885
+ if (promptText(text).length <= 280) return null;
886
+ const short = !opened.has(id);
887
+ return /* @__PURE__ */ jsx("p", {
888
+ className: "ar-prompt-more",
889
+ children: /* @__PURE__ */ jsx("button", {
890
+ type: "button",
891
+ className: "link-button",
892
+ "data-open-key": id,
893
+ "aria-expanded": !short,
894
+ onClick: () => setOpen(id, short),
895
+ children: short ? "Full prompt" : "Shorter"
896
+ })
897
+ });
898
+ }
899
+ /** Live values use the same canonical items and tool presentation as retained conversations. */
900
+ function timelineItems(entries, streaming, opened) {
901
+ const rows = foldRows(entries.map((event) => ({
902
+ kind: "event",
903
+ key: event.id,
904
+ event
905
+ })), (entry) => entry.kind === "message" && entry.role === "assistant" && entry.provenance !== "recorded-result" ? idleReply(entry.content) : null, (a, b) => a === b, (events) => ({
906
+ kind: "idle",
907
+ key: events[0].id,
908
+ events
909
+ }));
910
+ return rows.map((row, index) => {
911
+ const entry = row.kind === "event" ? row.event : row.events[0];
912
+ if (row.kind === "idle") return idleItem(entry.id, row.events.map((event) => ({
913
+ id: event.id,
914
+ at: Number.isFinite(event.at) ? new Date(event.at).toISOString() : void 0,
915
+ text: event.content
916
+ })));
917
+ if (entry.kind === "message") {
918
+ const body = entry.role === "user" ? promptText(entry.content) : entry.content;
919
+ const short = entry.role === "user" && opened && !opened.has(`prompt:${entry.id}`) && body.length > 280;
920
+ return {
921
+ id: entry.id,
922
+ kind: "message",
923
+ role: entry.role,
924
+ content: short ? `${body.slice(0, 280).trimEnd()}…` : body,
925
+ isStreaming: streaming && index === rows.length - 1,
926
+ ...entry.role === "user" && Number.isFinite(entry.at) ? { timestamp: new Date(entry.at) } : {}
927
+ };
928
+ }
929
+ if (entry.kind === "tool") return toolItem(entry.id, {
930
+ id: entry.id,
931
+ name: entry.name ?? entry.call.label,
932
+ input: entry.input === void 0 ? entry.call.detail : typeof entry.input === "string" ? entry.input : JSON.stringify(entry.input)
933
+ }, entry.call.output, entry.call.status === "error" ? "error" : entry.call.status === "running" ? streaming ? "pending" : "missing" : "returned", entry.call.duration ?? null, streaming, entry.costUsd);
934
+ if (entry.kind === "notice" && entry.isError) return {
935
+ id: entry.id,
936
+ kind: "status",
937
+ tone: "error",
938
+ label: "Execution error",
939
+ detail: entry.text
940
+ };
941
+ if (entry.kind === "notice") return {
942
+ id: entry.id,
943
+ kind: "custom",
944
+ content: /* @__PURE__ */ jsx("p", {
945
+ className: "text-xs text-muted-foreground",
946
+ children: entry.text
947
+ })
948
+ };
949
+ return {
950
+ id: entry.id,
951
+ kind: "custom",
952
+ content: /* @__PURE__ */ jsx(InlineThinkingItem, {
953
+ part: {
954
+ type: "reasoning",
955
+ text: entry.text
956
+ },
957
+ defaultOpen: false,
958
+ openWhileActive: false
959
+ })
960
+ };
961
+ });
962
+ }
880
963
  const toolNote = (part) => {
881
964
  const note = part.state.metadata?.note, time = part.state.time;
882
965
  const duration = time?.end != null && time.start != null ? formatSpanDuration(time.end - time.start) : "Not recorded";
@@ -940,30 +1023,14 @@ function pollItem(row) {
940
1023
  })
941
1024
  };
942
1025
  }
943
- /** Consecutive tool turns whose arguments and results were not captured: one row naming the calls. */
944
- const hollowItem = (row) => ({
945
- id: row.key,
946
- kind: "custom",
947
- content: /* @__PURE__ */ jsx(RunRowShell, {
948
- icon: /* @__PURE__ */ jsx(Settings, {
949
- size: 14,
950
- "aria-hidden": "true"
951
- }),
952
- title: hollowSummary(row.calls),
953
- description: "args not captured",
954
- durationMs: row.spanMs || void 0
955
- })
956
- });
957
- /** What one record row shows, as the kit's timeline items. */
958
- function rowItems(row, context) {
959
- if (row.kind === "polls") return [pollItem(row)];
960
- if (row.kind === "hollow") return [hollowItem(row)];
961
- if (row.kind === "idle") return [{
962
- id: `${row.key}:${context.selected ?? ""}`,
1026
+ /** The exact idle replies stay inspectable inside one shared fold in either conversation. */
1027
+ function idleItem(id, events, selected) {
1028
+ return {
1029
+ id,
963
1030
  kind: "fold",
964
- summary: `${row.events.length} idle turns · ${textOf(row.events[0])}`,
965
- defaultOpen: row.events.some((event) => event.id === context.selected),
966
- items: row.events.map((event) => ({
1031
+ summary: `${events.length} idle turns · ${events[0].text}`,
1032
+ defaultOpen: events.some((event) => event.id === selected),
1033
+ items: events.map((event) => ({
967
1034
  id: event.id,
968
1035
  kind: "custom",
969
1036
  content: /* @__PURE__ */ jsxs("div", {
@@ -971,16 +1038,38 @@ function rowItems(row, context) {
971
1038
  "data-message": event.id,
972
1039
  children: [/* @__PURE__ */ jsx("time", {
973
1040
  dateTime: event.at,
974
- children: utcTime(event.at)
1041
+ children: event.at ? utcTime(event.at) : "Unknown time"
975
1042
  }), /* @__PURE__ */ jsx(AgentTimelineStep, { item: {
976
1043
  id: event.id,
977
1044
  kind: "message",
978
1045
  role: "assistant",
979
- content: textOf(event)
1046
+ content: event.text
980
1047
  } })]
981
1048
  })
982
1049
  }))
1050
+ };
1051
+ }
1052
+ /** What one record row shows, as the kit's timeline items. */
1053
+ function rowItems(row, context) {
1054
+ if (row.kind === "polls") return [pollItem(row)];
1055
+ if (row.kind === "hollow") return [{
1056
+ id: row.key,
1057
+ kind: "custom",
1058
+ content: /* @__PURE__ */ jsx(RunRowShell, {
1059
+ icon: /* @__PURE__ */ jsx(Settings, {
1060
+ size: 14,
1061
+ "aria-hidden": "true"
1062
+ }),
1063
+ title: hollowSummary(row.calls),
1064
+ description: "args not captured",
1065
+ durationMs: row.spanMs || void 0
1066
+ })
983
1067
  }];
1068
+ if (row.kind === "idle") return [idleItem(`${row.key}:${context.selected ?? ""}`, row.events.map((event) => ({
1069
+ id: event.id,
1070
+ at: event.at,
1071
+ text: textOf(event)
1072
+ })), context.selected)];
984
1073
  const { index, cutoff, anchor, thinkingSpan, opened, paired, live } = context;
985
1074
  const event = row.event;
986
1075
  if (isLifecycle(event)) {
@@ -1085,8 +1174,6 @@ function rowItems(row, context) {
1085
1174
  }
1086
1175
  return items;
1087
1176
  }
1088
- /** The kind of a row's last item, which sets the gap above the next row. */
1089
- const lastKind = (items) => items.length ? agentTimelineStepKind(items[items.length - 1]) : void 0;
1090
1177
  function ConversationRowView({ row, items, previous, turn, context }) {
1091
1178
  const { selected, opened, anchor, setOpen, flagMarks, opener, flags } = context;
1092
1179
  const events = row.kind === "event" ? [row.event] : row.events;
@@ -1130,41 +1217,22 @@ function ConversationRowView({ row, items, previous, turn, context }) {
1130
1217
  const message = textOf(event);
1131
1218
  const standalone = event.detail.toolCallId !== void 0 || event.detail.role === "toolResult";
1132
1219
  const clip = clipOf(event.detail.clip);
1133
- if (event.detail.role === "user" && message && !standalone) {
1134
- const key = `prompt:${anchor(event)}`;
1135
- const long = promptText(message).length > 280;
1136
- const short = long && !opened.has(key);
1137
- return /* @__PURE__ */ jsxs("article", {
1138
- ...rowProps,
1139
- children: [
1140
- steps,
1141
- long && /* @__PURE__ */ jsx("p", {
1142
- className: "ar-prompt-more",
1143
- children: /* @__PURE__ */ jsx("button", {
1144
- type: "button",
1145
- className: "link-button",
1146
- "data-open-key": key,
1147
- "aria-expanded": !short,
1148
- onClick: () => setOpen(key, short),
1149
- children: short ? "Full prompt" : "Shorter"
1150
- })
1151
- }),
1152
- !short && /* @__PURE__ */ jsx(ClipNote, {
1153
- clip,
1154
- open: opener(event)
1155
- }),
1156
- flagMarks(event)
1157
- ]
1158
- });
1159
- }
1220
+ const prompt = event.detail.role === "user" && !!message && !standalone, key = `prompt:${anchor(event)}`;
1221
+ const short = prompt && promptText(message).length > 280 && !opened.has(key);
1160
1222
  const finding = event.detail.recordedClaim !== void 0;
1161
- const notes = [event.detail.publicationNote, finding ? event.detail.assessment : void 0].filter((note) => Boolean(note));
1223
+ const notes = (prompt ? [] : [event.detail.publicationNote, finding ? event.detail.assessment : void 0]).filter((note) => Boolean(note));
1162
1224
  return /* @__PURE__ */ jsxs("article", {
1163
1225
  ...rowProps,
1164
1226
  children: [
1165
1227
  steps,
1166
- message && !standalone && /* @__PURE__ */ jsx(ClipNote, {
1167
- clip: callsOf(event).length ? void 0 : clip,
1228
+ prompt && /* @__PURE__ */ jsx(PromptMore, {
1229
+ text: message,
1230
+ id: key,
1231
+ opened,
1232
+ setOpen
1233
+ }),
1234
+ message && !standalone && !short && /* @__PURE__ */ jsx(ClipNote, {
1235
+ clip: !prompt && callsOf(event).length ? void 0 : clip,
1168
1236
  open: opener(event)
1169
1237
  }),
1170
1238
  notes.map((note) => /* @__PURE__ */ jsx("p", {
@@ -1175,313 +1243,4 @@ function ConversationRowView({ row, items, previous, turn, context }) {
1175
1243
  ]
1176
1244
  });
1177
1245
  }
1178
- // src/run/record/Conversation.tsx
1179
- function Conversation({ index, actor, cutoff, query, category, selected, openSource, flags, onFlag, lifecycle = false, live = false, follow = live, opening }) {
1180
- const [opened, setOpened] = useState(() => {
1181
- const at = anchorsOf(index.events);
1182
- return new Set(index.events.filter((event) => event.detail.promptKind === "initial").map((event) => `prompt:${at.get(event.id) ?? event.id}`));
1183
- });
1184
- const scroll = useRef(null);
1185
- const anchors = useMemo(() => anchorsOf(index.events), [index]);
1186
- const anchor = useCallback((event) => anchors.get(event.id) ?? event.id, [anchors]);
1187
- const opener = (event) => openSource ? (sha256) => openSource(sha256, event) : void 0;
1188
- const setOpen = (key, open) => setOpened((current) => {
1189
- if (current.has(key) === open) return current;
1190
- const next = new Set(current);
1191
- if (open) next.add(key);
1192
- else next.delete(key);
1193
- return next;
1194
- });
1195
- const paired = (event, call) => {
1196
- const key = toolKey(event.node, call.id);
1197
- return index.calls.get(key)?.length === 1 ? index.results.get(key) ?? [] : [];
1198
- };
1199
- const target = useMemo(() => {
1200
- if (!selected) return void 0;
1201
- const event = index.byEvent.get(selected);
1202
- if (event?.detail.toolCallId !== void 0) {
1203
- const calls = index.calls.get(toolKey(event.node, event.detail.toolCallId));
1204
- if (calls?.length === 1) return calls[0].event.id;
1205
- }
1206
- return selected;
1207
- }, [selected, index]);
1208
- const items = useMemo(() => (index.byActor.get(actor) ?? []).filter((event) => {
1209
- if (ms(event.at) > cutoff) return false;
1210
- if (event.id === target) return true;
1211
- if (isLifecycle(event)) return lifecycle && eventMatches(event, index, query, category);
1212
- if (event.detail.toolCallId !== void 0) {
1213
- const matching = index.calls.get(toolKey(event.node, event.detail.toolCallId));
1214
- if (matching?.length === 1 && ms(matching[0].event.at) <= cutoff) return false;
1215
- }
1216
- const calls = callsOf(event);
1217
- if (!(event.detail.role || textOf(event) || event.detail.reasoning || event.detail.responseStatus === "error" || calls.length)) return false;
1218
- if (event.detail.role === "assistant" && !textOf(event) && !event.detail.reasoning && !calls.length && event.detail.responseStatus !== "error" && !event.detail.contentOmitted && (!thinkingMarkers(event.detail).length || quietThinking(event.detail))) return false;
1219
- if (eventMatches(event, index, query, category)) return true;
1220
- return calls.some((call) => {
1221
- const key = toolKey(event.node, call.id);
1222
- return index.calls.get(key)?.length === 1 && (index.results.get(key) ?? []).some((result) => ms(result.at) <= cutoff && eventMatches(result, index, query, category));
1223
- });
1224
- }), [
1225
- index,
1226
- actor,
1227
- cutoff,
1228
- query,
1229
- category,
1230
- lifecycle,
1231
- target
1232
- ]);
1233
- const shown = useMemo(() => {
1234
- if (!opening?.text || query || category !== "all" || items.some((event) => event.detail.role === "user" && event.detail.promptKind !== "steering")) return items;
1235
- const first = items[0]?.at ?? opening.at;
1236
- return [{
1237
- id: `${actor}:opening`,
1238
- node: actor,
1239
- at: first < opening.at ? first : opening.at,
1240
- kind: "message",
1241
- category: "other",
1242
- label: "prompt",
1243
- detail: {
1244
- role: "user",
1245
- publicText: opening.text,
1246
- promptKind: "initial"
1247
- }
1248
- }, ...items];
1249
- }, [
1250
- items,
1251
- opening,
1252
- actor,
1253
- query,
1254
- category
1255
- ]);
1256
- const rows = useMemo(() => collapseIdleTurns(collapseHollowTurns(collapsePolls(shown, {
1257
- callsOf,
1258
- answered: (event, id) => answeredBy(index.results.get(toolKey(event.node, id))),
1259
- keyOf: anchor
1260
- }), {
1261
- callsOf,
1262
- captured: (event, id) => (index.results.get(toolKey(event.node, id)) ?? []).some((result) => textOf(result)),
1263
- keyOf: anchor
1264
- }), callsOf), [
1265
- shown,
1266
- index,
1267
- anchor
1268
- ]);
1269
- const thinkingSpan = useMemo(() => {
1270
- const spans = /* @__PURE__ */ new Map();
1271
- let previous = null;
1272
- for (const event of index.byActor.get(actor) ?? []) {
1273
- const at = ms(event.at);
1274
- if (previous !== null && Number.isFinite(at) && thinkingMarkers(event.detail).length) spans.set(event.id, Math.max(0, at - previous));
1275
- if (Number.isFinite(at)) previous = at;
1276
- }
1277
- return spans;
1278
- }, [index, actor]);
1279
- const rowOf = useMemo(() => {
1280
- const map = /* @__PURE__ */ new Map();
1281
- for (const row of rows) if (row.kind !== "event") for (const event of row.events) map.set(event.id, row.key);
1282
- else map.set(row.event.id, row.key);
1283
- return map;
1284
- }, [rows]);
1285
- const retained = (index.byActor.get(actor) ?? []).some((item) => !isLifecycle(item) && (item.detail.role || textOf(item) || callsOf(item).length));
1286
- const { following } = useFollowTail(scroll, {
1287
- identity: actor,
1288
- content: rows,
1289
- follow,
1290
- target
1291
- });
1292
- const arrival = useRef({ actor });
1293
- useLayoutEffect(() => {
1294
- const tail = rows.at(-1), event = tail?.kind === "event" ? tail.event : tail?.events.at(-1), entry = tail?.kind === "event" ? tail.event.id : tail?.events[0]?.id;
1295
- let animate = live && arrival.current.actor === actor && !!arrival.current.id && arrival.current.id !== event?.id;
1296
- arrival.current = {
1297
- actor,
1298
- id: event?.id
1299
- };
1300
- const element = scroll.current;
1301
- if (!element || !following.current) return;
1302
- const frame = requestAnimationFrame(() => {
1303
- if (!following.current) return;
1304
- const row = entry && element.querySelector(`[data-entry="${CSS.escape(entry)}"]`);
1305
- if (animate && row) {
1306
- animate = false;
1307
- const style = getComputedStyle(element), value = style.getPropertyValue("--duration-stream").trim(), duration = parseFloat(value) * (value.endsWith("ms") ? 1 : 1e3);
1308
- if (duration > 0 && !matchMedia("(prefers-reduced-motion: reduce)").matches) row.animate([{ opacity: 0 }, { opacity: 1 }], {
1309
- duration,
1310
- easing: style.getPropertyValue("--ease-expo").trim() || "linear"
1311
- });
1312
- }
1313
- });
1314
- return () => cancelAnimationFrame(frame);
1315
- }, [rows]);
1316
- useEffect(() => {
1317
- if (!selected) return;
1318
- const event = index.byEvent.get(selected);
1319
- if (!event) return;
1320
- const keys = [`prompt:${anchor(event)}`, `reasoning:${anchor(event)}`];
1321
- if (event.detail.toolCallId !== void 0) for (const match of index.calls.get(toolKey(event.node, event.detail.toolCallId)) ?? []) keys.push(`tool:${anchor(match.event)}:${match.call.id}`, rowOf.get(match.event.id) ?? "");
1322
- for (const call of callsOf(event)) keys.push(`tool:${anchor(event)}:${call.id}`);
1323
- const row = rowOf.get(selected);
1324
- if (row?.startsWith("polls:")) keys.push(row);
1325
- setOpened((current) => /* @__PURE__ */ new Set([...current, ...keys.filter(Boolean)]));
1326
- }, [selected, index]);
1327
- const flagMarks = (event) => {
1328
- const marks = flags?.get(event.id);
1329
- if (!marks?.length) return null;
1330
- return /* @__PURE__ */ jsx("span", {
1331
- className: "event-flags",
1332
- children: marks.map((flag) => /* @__PURE__ */ jsx("button", {
1333
- type: "button",
1334
- className: `event-flag polarity-${flag.polarity}`,
1335
- title: flag.title,
1336
- "data-flag": flag.dimension,
1337
- onClick: () => onFlag?.(flag, event),
1338
- children: flag.dimension
1339
- }, `${flag.dimension}:${flag.label}`))
1340
- });
1341
- };
1342
- const keyOf = useCallback((row) => row.key, []);
1343
- const estimate = useCallback((row) => {
1344
- if (row.kind !== "event") return 36;
1345
- if (isLifecycle(row.event)) return 26;
1346
- return !textOf(row.event) && callsOf(row.event).length ? 16 + 36 * callsOf(row.event).length : 96;
1347
- }, []);
1348
- const context = {
1349
- index,
1350
- cutoff,
1351
- selected,
1352
- opened,
1353
- anchor,
1354
- opener,
1355
- setOpen,
1356
- flagMarks,
1357
- paired,
1358
- thinkingSpan,
1359
- flags,
1360
- live
1361
- };
1362
- const itemsOf = useMemo(() => rows.map((row) => rowItems(row, context)), [
1363
- rows,
1364
- opened,
1365
- cutoff,
1366
- selected,
1367
- live,
1368
- index,
1369
- thinkingSpan
1370
- ]);
1371
- const turns = useMemo(() => {
1372
- let previous;
1373
- return rows.map((row) => {
1374
- const event = row.kind === "event" ? row.event : row.events[0];
1375
- const turn = event.detail.role === "assistant" && !isLifecycle(event) && (!previous || previous.detail.role === "user" || (event.detail.attempt !== void 0 ? event.detail.attempt !== previous.detail.attempt : ms(event.at) - ms(previous.at) > 12e4));
1376
- previous = row.kind === "event" ? event : row.events.at(-1);
1377
- return turn;
1378
- });
1379
- }, [rows]);
1380
- const render = (row, i) => /* @__PURE__ */ jsx(ConversationRowView, {
1381
- row,
1382
- items: itemsOf[i] ?? [],
1383
- previous: i ? lastKind(itemsOf[i - 1] ?? []) : void 0,
1384
- turn: turns[i],
1385
- context
1386
- });
1387
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", {
1388
- className: "record-conversation conversation-scroll",
1389
- "data-chat": true,
1390
- ref: scroll,
1391
- tabIndex: 0,
1392
- "aria-label": "Scrollable retained conversation",
1393
- children: [!rows.length && /* @__PURE__ */ jsx("p", {
1394
- className: "chat-empty",
1395
- children: query || category !== "all" ? "No messages match this filter." : retained ? "No messages at the selected time." : "Conversation not retained. Its absence does not mean the agent did no work."
1396
- }), /* @__PURE__ */ jsx(AgentTimelineColumn, {
1397
- className: "ar-chat",
1398
- children: /* @__PURE__ */ jsx(VirtualList, {
1399
- items: rows,
1400
- keyOf,
1401
- render,
1402
- scroller: scroll,
1403
- target: target ? rowOf.get(target) : void 0,
1404
- estimate
1405
- })
1406
- })]
1407
- }) });
1408
- }
1409
- // src/run/record/AgentTree.tsx
1410
- /** Retained identity and recorded time define the hierarchy; labels never join sessions or invent parents. */
1411
- function AgentTree({ index, actor, cutoff, select }) {
1412
- const roots = useMemo(() => {
1413
- const nodes = index.actors.filter((node) => node.kind !== "finding");
1414
- const byId = new Map(nodes.map((node) => [node.id, node]));
1415
- const children = /* @__PURE__ */ new Map();
1416
- for (const node of nodes) {
1417
- if (!node.parent) continue;
1418
- const parent = index.canonical(node.parent), siblings = children.get(parent) ?? [];
1419
- siblings.push(node);
1420
- children.set(parent, siblings);
1421
- }
1422
- const visited = /* @__PURE__ */ new Set();
1423
- const visit = (node) => {
1424
- if (visited.has(node.id)) return [];
1425
- visited.add(node.id);
1426
- return [{
1427
- node,
1428
- children: (children.get(node.id) ?? []).flatMap(visit)
1429
- }];
1430
- };
1431
- const roots = nodes.filter((node) => !node.parent || !byId.has(index.canonical(node.parent))).flatMap(visit);
1432
- for (const node of nodes) roots.push(...visit(node));
1433
- return roots;
1434
- }, [index]);
1435
- const renderNodes = (entries, nested = false) => /* @__PURE__ */ jsx("ul", {
1436
- className: nested ? "m-0 ml-3 list-none border-l border-border pl-2" : "m-0 grid list-none gap-1 p-0",
1437
- children: entries.map(({ node, children }) => {
1438
- const firstEvent = index.byActor.get(node.id)?.[0];
1439
- const begin = node.start ? ms(node.start) : firstEvent ? ms(firstEvent.at) : void 0;
1440
- const future = begin !== void 0 && begin > cutoff, pending = typeof node.end === "string" && ms(node.end) > cutoff;
1441
- const status = runStatus(future || pending ? "unknown" : node.status ?? "unknown");
1442
- const state = future ? "Not yet started" : pending ? "Not yet settled" : statusMeta(status).label;
1443
- return /* @__PURE__ */ jsxs("li", {
1444
- className: "min-w-0",
1445
- children: [/* @__PURE__ */ jsxs("button", {
1446
- type: "button",
1447
- className: "flex w-full min-w-0 items-start gap-2 rounded border border-transparent px-2 py-1.5 text-left text-sm text-foreground hover:border-border focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring aria-pressed:border-border aria-pressed:bg-muted data-[future]:opacity-55",
1448
- "aria-label": "Read " + roleOf(node) + " " + node.label + " conversation. " + state + ".",
1449
- "aria-pressed": actor === node.id,
1450
- "data-node": node.id,
1451
- "data-future": future ? "" : void 0,
1452
- title: node.label + (node.assignment ? " · " + node.assignment : "") + " · " + state,
1453
- onClick: () => select(node.id),
1454
- children: [/* @__PURE__ */ jsx(RunStatusIcon, {
1455
- status,
1456
- className: "mt-0.5 size-3.5 shrink-0",
1457
- "aria-hidden": true
1458
- }), /* @__PURE__ */ jsxs("span", {
1459
- className: "grid min-w-0 gap-0.5 break-words",
1460
- children: [/* @__PURE__ */ jsx("span", {
1461
- className: "font-semibold",
1462
- children: node.label
1463
- }), /* @__PURE__ */ jsxs("span", {
1464
- className: "text-muted-foreground",
1465
- children: [
1466
- roleOf(node),
1467
- " · ",
1468
- state
1469
- ]
1470
- })]
1471
- })]
1472
- }), children.length > 0 && renderNodes(children, true)]
1473
- }, node.id);
1474
- })
1475
- });
1476
- return /* @__PURE__ */ jsx("div", {
1477
- className: "record-agent-tree max-h-80 overflow-auto px-2 py-2",
1478
- "data-graph": true,
1479
- role: "group",
1480
- "aria-label": "Recorded agent hierarchy. Select an agent to read its conversation.",
1481
- children: roots.length ? renderNodes(roots) : /* @__PURE__ */ jsx("p", {
1482
- className: "text-sm text-muted-foreground",
1483
- children: "No agent topology was retained."
1484
- })
1485
- });
1486
- }
1487
- export { fmt as a, modelIdentity as c, recordFlowTrace as d, roleOf as f, utcTime as h, eventSource as i, model_exports as l, toolKey as m, Conversation as n, indexRecord as o, textOf as p, eventMatches as r, interval as s, AgentTree as t, ms as u };
1246
+ export { ms as C, toolKey as D, textOf as E, utcTime as O, model_exports as S, roleOf as T, eventSource as _, isLifecycle as a, interval as b, anchorsOf as c, collapseIdleTurns as d, collapsePolls as f, eventMatches as g, callsOf as h, ToolStep as i, answeredBy as l, thinkingMarkers as m, ConversationRowView as n, rowItems as o, quietThinking as p, PromptMore as r, timelineItems as s, useFollowTail as t, collapseHollowTurns as u, fmt as v, recordFlowTrace as w, modelIdentity as x, indexRecord as y };
@@ -1 +1 @@
1
- export { r as VAULT_TREE_CHILD_PAGE, i as VaultTree, t as fileTreeFromPaths, n as filterFileNodes } from "./tree-data-9D309DqD.js";
1
+ export { r as VAULT_TREE_CHILD_PAGE, i as VaultTree, t as fileTreeFromPaths, n as filterFileNodes } from "./tree-data-rg5voiY8.js";
@@ -1,7 +1,7 @@
1
1
  import { t as cn$1 } from "./utils-BER_bdgE.js";
2
2
  import { r as ShellHeader } from "./shell-header-CwqDzC4I.js";
3
3
  import { t as MOTION_CONTROL } from "./motion-2Ine0j_s.js";
4
- import "./workspace-files-pane-Di_rn4FL.js";
4
+ import "./workspace-files-pane-D24G7H_Z.js";
5
5
  import { ArtifactPane, ArtifactPane as ArtifactPane$1, EmptyState, Input, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@tangle-network/ui/primitives";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { Markdown } from "@tangle-network/ui/markdown";
@@ -1,5 +1,5 @@
1
1
  import { t as cn } from "./utils-BER_bdgE.js";
2
- import { i as VaultTree, n as filterFileNodes, t as fileTreeFromPaths } from "./tree-data-9D309DqD.js";
2
+ import { i as VaultTree, n as filterFileNodes, t as fileTreeFromPaths } from "./tree-data-rg5voiY8.js";
3
3
  import { Button, EmptyState, Input, SegmentedControl } from "@tangle-network/ui/primitives";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { useEffect, useId, useMemo, useRef, useState } from "react";