@tangle-network/sandbox-ui 3.0.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +0 -47
  2. package/dist/AgentTree-D4d06rfH.js +293 -0
  3. package/dist/{ResearchReport-vqefUbVp.js → ResearchReport-D1TFmldm.js} +2 -1
  4. package/dist/{VirtualList-CMynqkkh.js → VirtualList-DcSGbEtA.js} +28 -1
  5. package/dist/globals.css +12 -3
  6. package/dist/{index-gXcuwPOX.d.ts → index-Bv2iiZaN.d.ts} +135 -2
  7. package/dist/{index-YU_b9xCK.d.ts → index-CHA38az4.d.ts} +3 -61
  8. package/dist/{index-CQGMhSg0.d.ts → index-Z6RCpkmT.d.ts} +16 -1
  9. package/dist/index.d.ts +4 -5
  10. package/dist/index.js +10 -10
  11. package/dist/record/ResearchReport.js +1 -1
  12. package/dist/{record-B_4XWfUz.js → record-C-EUpK-n.js} +3 -2
  13. package/dist/record.js +1 -1
  14. package/dist/report-viewer.js +80 -80
  15. package/dist/run/live.js +2 -2
  16. package/dist/run/record.d.ts +2 -2
  17. package/dist/run/record.js +5 -4
  18. package/dist/run.d.ts +3 -3
  19. package/dist/run.js +6 -5
  20. package/dist/runtime-viewer/{AgentGraphCanvas-DGq7_u8--WPFPgd72.js → AgentGraphCanvas-DGq7_u8--C6yDfIUT.js} +67 -67
  21. package/dist/runtime-viewer/{latest-events-CRq9sGkK-YFXUh9qO.js → latest-events-CRq9sGkK-CfoBl1dP.js} +215 -209
  22. package/dist/styles.css +12 -3
  23. package/dist/{trace-workspace-BiJbv9WQ.js → trace-workspace-B634-EcA.js} +287 -201
  24. package/dist/{tree-data-9D309DqD.js → tree-data-rg5voiY8.js} +1 -1
  25. package/dist/{AgentTree-Cju8jn51.js → use-follow-tail-DZFn7QDO.js} +144 -385
  26. package/dist/vault-tree.js +1 -1
  27. package/dist/{workbench-Z9wbei53.js → workbench-DWQ0pm9w.js} +311 -2
  28. package/dist/workbench.d.ts +2 -2
  29. package/dist/workbench.js +2 -2
  30. package/dist/{workspace-BrS_rHWN.js → workspace-CJD7oOEa.js} +1 -1
  31. package/dist/{workspace-files-pane-Di_rn4FL.js → workspace-files-pane-D24G7H_Z.js} +1 -1
  32. package/dist/workspace-viewer.js +9970 -9624
  33. package/dist/workspace.js +2 -2
  34. package/package.json +5 -8
  35. package/dist/connections-DymjzHe_.js +0 -179
  36. package/dist/connections.d.ts +0 -2
  37. package/dist/connections.js +0 -1
  38. package/dist/index-DLX35YAa.d.ts +0 -59
  39. package/dist/use-follow-tail-Ht7fZGQQ.js +0 -1
package/README.md CHANGED
@@ -120,53 +120,6 @@ Pass service-observed `readiness` and enforced `access` to show their status ins
120
120
  Show process and port state beside the pane if your runtime exposes those separately.
121
121
  Do not infer either state from the preview URL or iframe load.
122
122
 
123
- ### Show a shared agent messaging connection
124
-
125
- Use `AgentMessagingConnection` in any app that enrolls a member with a private agent target and a shared messaging route.
126
- The host owns authentication, the member grant, canonical target resolution, and route readiness.
127
-
128
- ```tsx
129
- import {
130
- AgentMessagingConnection,
131
- type AgentEnrollmentViewState,
132
- type SharedMessagingRouteViewState,
133
- } from "@tangle-network/sandbox-ui/connections";
134
-
135
- type MessagingConnectionProps = {
136
- enrollment: AgentEnrollmentViewState;
137
- route: SharedMessagingRouteViewState;
138
- enroll: () => Promise<void>;
139
- openMessages: () => void;
140
- refresh: () => Promise<void>;
141
- };
142
-
143
- export function MessagingConnection({
144
- enrollment,
145
- route,
146
- enroll,
147
- openMessages,
148
- refresh,
149
- }: MessagingConnectionProps) {
150
- return (
151
- <AgentMessagingConnection
152
- enrollment={enrollment}
153
- route={route}
154
- onEnroll={enroll}
155
- onOpenMessages={openMessages}
156
- onRetry={refresh}
157
- />
158
- );
159
- }
160
- ```
161
-
162
- Report `eligible` only after the host checks the member's current grant.
163
- Report `enrolled` only after it resolves the authorized, canonical private agent target.
164
- Give the component display labels, not raw enrollment, instance, sandbox, or session IDs.
165
- Report route `ready` only when the shared route is valid for this application and member.
166
- An attached provider line on its own does not prove that state.
167
- The component never attaches, buys, or reassigns a provider identity.
168
- It shows a messaging action only when both the target and route are confirmed.
169
-
170
123
  ### Fonts
171
124
 
172
125
  sandbox-ui references the following font families in its design tokens but does **not** bundle them — consumer apps must load the fonts themselves. This is deliberate: a URL `@import` inside a library CSS bundle breaks when downstream apps chain-import the stylesheet (see CHANGELOG 0.10.9 for the full reasoning).
@@ -0,0 +1,293 @@
1
+ import { t as VirtualList } from "./VirtualList-DcSGbEtA.js";
2
+ import { C as ms, D as toolKey, E as textOf, T as roleOf, a as isLifecycle, c as anchorsOf, d as collapseIdleTurns, f as collapsePolls, g as eventMatches, h as callsOf, l as answeredBy, m as thinkingMarkers, n as ConversationRowView, o as rowItems, p as quietThinking, t as useFollowTail, u as collapseHollowTurns } from "./use-follow-tail-DZFn7QDO.js";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
+ import { AgentTimelineColumn, agentTimelineStepKind } from "@tangle-network/ui/chat/agent-timeline";
6
+ import { RunStatusIcon, runStatus, statusMeta } from "@tangle-network/ui/run/status-meta";
7
+ // src/run/record/Conversation.tsx
8
+ function Conversation({ index, actor, cutoff, query, category, selected, openSource, flags, onFlag, lifecycle = false, live = false, follow = live, opening }) {
9
+ const [opened, setOpened] = useState(() => {
10
+ const at = anchorsOf(index.events);
11
+ return new Set(index.events.filter((event) => event.detail.promptKind === "initial").map((event) => `prompt:${at.get(event.id) ?? event.id}`));
12
+ });
13
+ const scroll = useRef(null);
14
+ const anchors = useMemo(() => anchorsOf(index.events), [index]);
15
+ const anchor = useCallback((event) => anchors.get(event.id) ?? event.id, [anchors]);
16
+ const opener = (event) => openSource ? (sha256) => openSource(sha256, event) : void 0;
17
+ const setOpen = (key, open) => setOpened((current) => {
18
+ if (current.has(key) === open) return current;
19
+ const next = new Set(current);
20
+ if (open) next.add(key);
21
+ else next.delete(key);
22
+ return next;
23
+ });
24
+ const paired = (event, call) => {
25
+ const key = toolKey(event.node, call.id);
26
+ return index.calls.get(key)?.length === 1 ? index.results.get(key) ?? [] : [];
27
+ };
28
+ const target = useMemo(() => {
29
+ if (!selected) return void 0;
30
+ const event = index.byEvent.get(selected);
31
+ if (event?.detail.toolCallId !== void 0) {
32
+ const calls = index.calls.get(toolKey(event.node, event.detail.toolCallId));
33
+ if (calls?.length === 1) return calls[0].event.id;
34
+ }
35
+ return selected;
36
+ }, [selected, index]);
37
+ const items = useMemo(() => (index.byActor.get(actor) ?? []).filter((event) => {
38
+ if (ms(event.at) > cutoff) return false;
39
+ if (event.id === target) return true;
40
+ if (isLifecycle(event)) return lifecycle && eventMatches(event, index, query, category);
41
+ if (event.detail.toolCallId !== void 0) {
42
+ const matching = index.calls.get(toolKey(event.node, event.detail.toolCallId));
43
+ if (matching?.length === 1 && ms(matching[0].event.at) <= cutoff) return false;
44
+ }
45
+ const calls = callsOf(event);
46
+ if (!(event.detail.role || textOf(event) || event.detail.reasoning || event.detail.responseStatus === "error" || calls.length)) return false;
47
+ 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;
48
+ if (eventMatches(event, index, query, category)) return true;
49
+ return calls.some((call) => {
50
+ const key = toolKey(event.node, call.id);
51
+ return index.calls.get(key)?.length === 1 && (index.results.get(key) ?? []).some((result) => ms(result.at) <= cutoff && eventMatches(result, index, query, category));
52
+ });
53
+ }), [
54
+ index,
55
+ actor,
56
+ cutoff,
57
+ query,
58
+ category,
59
+ lifecycle,
60
+ target
61
+ ]);
62
+ const shown = useMemo(() => {
63
+ if (!opening?.text || query || category !== "all" || items.some((event) => event.detail.role === "user" && event.detail.promptKind !== "steering")) return items;
64
+ const first = items[0]?.at ?? opening.at;
65
+ return [{
66
+ id: `${actor}:opening`,
67
+ node: actor,
68
+ at: first < opening.at ? first : opening.at,
69
+ kind: "message",
70
+ category: "other",
71
+ label: "prompt",
72
+ detail: {
73
+ role: "user",
74
+ publicText: opening.text,
75
+ promptKind: "initial"
76
+ }
77
+ }, ...items];
78
+ }, [
79
+ items,
80
+ opening,
81
+ actor,
82
+ query,
83
+ category
84
+ ]);
85
+ const rows = useMemo(() => collapseIdleTurns(collapseHollowTurns(collapsePolls(shown, {
86
+ callsOf,
87
+ answered: (event, id) => answeredBy(index.results.get(toolKey(event.node, id))),
88
+ keyOf: anchor
89
+ }), {
90
+ callsOf,
91
+ captured: (event, id) => (index.results.get(toolKey(event.node, id)) ?? []).some((result) => textOf(result)),
92
+ keyOf: anchor
93
+ }), callsOf), [
94
+ shown,
95
+ index,
96
+ anchor
97
+ ]);
98
+ const thinkingSpan = useMemo(() => {
99
+ const spans = /* @__PURE__ */ new Map();
100
+ let previous = null;
101
+ for (const event of index.byActor.get(actor) ?? []) {
102
+ const at = ms(event.at);
103
+ if (previous !== null && Number.isFinite(at) && thinkingMarkers(event.detail).length) spans.set(event.id, Math.max(0, at - previous));
104
+ if (Number.isFinite(at)) previous = at;
105
+ }
106
+ return spans;
107
+ }, [index, actor]);
108
+ const rowOf = useMemo(() => new Map(rows.flatMap((row) => (row.kind === "event" ? [row.event] : row.events).map((event) => [event.id, row.key]))), [rows]);
109
+ const retained = (index.byActor.get(actor) ?? []).some((item) => !isLifecycle(item) && (item.detail.role || textOf(item) || callsOf(item).length));
110
+ const tail = rows.at(-1), event = tail?.kind === "event" ? tail.event : tail?.events.at(-1);
111
+ const { following } = useFollowTail(scroll, {
112
+ identity: actor,
113
+ content: rows,
114
+ follow,
115
+ target
116
+ });
117
+ useEffect(() => {
118
+ if (!selected) return;
119
+ const event = index.byEvent.get(selected);
120
+ if (!event) return;
121
+ const keys = [`prompt:${anchor(event)}`, `reasoning:${anchor(event)}`];
122
+ 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) ?? "");
123
+ for (const call of callsOf(event)) keys.push(`tool:${anchor(event)}:${call.id}`);
124
+ const row = rowOf.get(selected);
125
+ if (row?.startsWith("polls:")) keys.push(row);
126
+ setOpened((current) => /* @__PURE__ */ new Set([...current, ...keys.filter(Boolean)]));
127
+ }, [selected, index]);
128
+ const flagMarks = (event) => {
129
+ const marks = flags?.get(event.id);
130
+ if (!marks?.length) return null;
131
+ return /* @__PURE__ */ jsx("span", {
132
+ className: "event-flags",
133
+ children: marks.map((flag) => /* @__PURE__ */ jsx("button", {
134
+ type: "button",
135
+ className: `event-flag polarity-${flag.polarity}`,
136
+ title: flag.title,
137
+ "data-flag": flag.dimension,
138
+ onClick: () => onFlag?.(flag, event),
139
+ children: flag.dimension
140
+ }, `${flag.dimension}:${flag.label}`))
141
+ });
142
+ };
143
+ const keyOf = useCallback((row) => row.key, []);
144
+ const estimate = useCallback((row) => {
145
+ if (row.kind !== "event") return 36;
146
+ if (isLifecycle(row.event)) return 26;
147
+ return !textOf(row.event) && callsOf(row.event).length ? 16 + 36 * callsOf(row.event).length : 96;
148
+ }, []);
149
+ const context = {
150
+ index,
151
+ cutoff,
152
+ selected,
153
+ opened,
154
+ anchor,
155
+ opener,
156
+ setOpen,
157
+ flagMarks,
158
+ paired,
159
+ thinkingSpan,
160
+ flags,
161
+ live
162
+ };
163
+ const itemsOf = useMemo(() => rows.map((row) => rowItems(row, context)), [
164
+ rows,
165
+ opened,
166
+ cutoff,
167
+ selected,
168
+ live,
169
+ index,
170
+ thinkingSpan
171
+ ]);
172
+ const turns = useMemo(() => {
173
+ let previous;
174
+ return rows.map((row) => {
175
+ const event = row.kind === "event" ? row.event : row.events[0];
176
+ 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));
177
+ previous = row.kind === "event" ? event : row.events.at(-1);
178
+ return turn;
179
+ });
180
+ }, [rows]);
181
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", {
182
+ className: "record-conversation conversation-scroll",
183
+ "data-chat": true,
184
+ ref: scroll,
185
+ tabIndex: 0,
186
+ "aria-label": "Scrollable retained conversation",
187
+ children: [!rows.length && /* @__PURE__ */ jsx("p", {
188
+ className: "chat-empty",
189
+ 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."
190
+ }), /* @__PURE__ */ jsx(AgentTimelineColumn, {
191
+ className: "ar-chat",
192
+ children: /* @__PURE__ */ jsx(VirtualList, {
193
+ items: rows,
194
+ keyOf,
195
+ render: (row, i) => /* @__PURE__ */ jsx(ConversationRowView, {
196
+ row,
197
+ items: itemsOf[i] ?? [],
198
+ previous: i && itemsOf[i - 1]?.length ? agentTimelineStepKind(itemsOf[i - 1].at(-1)) : void 0,
199
+ turn: turns[i],
200
+ context
201
+ }),
202
+ scroller: scroll,
203
+ target: target ? rowOf.get(target) : void 0,
204
+ estimate,
205
+ arrival: live ? {
206
+ identity: actor,
207
+ id: event?.id,
208
+ key: tail?.key,
209
+ following
210
+ } : void 0
211
+ })
212
+ })]
213
+ }) });
214
+ }
215
+ // src/run/record/AgentTree.tsx
216
+ /** Retained identity and recorded time define the hierarchy; labels never join sessions or invent parents. */
217
+ function AgentTree({ index, actor, cutoff, select }) {
218
+ const roots = useMemo(() => {
219
+ const nodes = index.actors.filter((node) => node.kind !== "finding");
220
+ const byId = new Map(nodes.map((node) => [node.id, node]));
221
+ const children = /* @__PURE__ */ new Map();
222
+ for (const node of nodes) {
223
+ if (!node.parent) continue;
224
+ const parent = index.canonical(node.parent), siblings = children.get(parent) ?? [];
225
+ siblings.push(node);
226
+ children.set(parent, siblings);
227
+ }
228
+ const visited = /* @__PURE__ */ new Set();
229
+ const visit = (node) => {
230
+ if (visited.has(node.id)) return [];
231
+ visited.add(node.id);
232
+ return [{
233
+ node,
234
+ children: (children.get(node.id) ?? []).flatMap(visit)
235
+ }];
236
+ };
237
+ const roots = nodes.filter((node) => !node.parent || !byId.has(index.canonical(node.parent))).flatMap(visit);
238
+ for (const node of nodes) roots.push(...visit(node));
239
+ return roots;
240
+ }, [index]);
241
+ const renderNodes = (entries, nested = false) => /* @__PURE__ */ jsx("ul", {
242
+ className: nested ? "m-0 ml-3 list-none border-l border-border pl-2" : "m-0 grid list-none gap-1 p-0",
243
+ children: entries.map(({ node, children }) => {
244
+ const firstEvent = index.byActor.get(node.id)?.[0];
245
+ const begin = node.start ? ms(node.start) : firstEvent ? ms(firstEvent.at) : void 0;
246
+ const future = begin !== void 0 && begin > cutoff, pending = typeof node.end === "string" && ms(node.end) > cutoff;
247
+ const status = runStatus(future || pending ? "unknown" : node.status ?? "unknown");
248
+ const state = future ? "Not yet started" : pending ? "Not yet settled" : statusMeta(status).label;
249
+ return /* @__PURE__ */ jsxs("li", {
250
+ className: "min-w-0",
251
+ children: [/* @__PURE__ */ jsxs("button", {
252
+ type: "button",
253
+ 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",
254
+ "aria-label": "Read " + roleOf(node) + " " + node.label + " conversation. " + state + ".",
255
+ "aria-pressed": actor === node.id,
256
+ "data-node": node.id,
257
+ "data-future": future ? "" : void 0,
258
+ title: node.label + (node.assignment ? " · " + node.assignment : "") + " · " + state,
259
+ onClick: () => select(node.id),
260
+ children: [/* @__PURE__ */ jsx(RunStatusIcon, {
261
+ status,
262
+ className: "mt-0.5 size-3.5 shrink-0",
263
+ "aria-hidden": true
264
+ }), /* @__PURE__ */ jsxs("span", {
265
+ className: "grid min-w-0 gap-0.5 break-words",
266
+ children: [/* @__PURE__ */ jsx("span", {
267
+ className: "font-semibold",
268
+ children: node.label
269
+ }), /* @__PURE__ */ jsxs("span", {
270
+ className: "text-muted-foreground",
271
+ children: [
272
+ roleOf(node),
273
+ " · ",
274
+ state
275
+ ]
276
+ })]
277
+ })]
278
+ }), children.length > 0 && renderNodes(children, true)]
279
+ }, node.id);
280
+ })
281
+ });
282
+ return /* @__PURE__ */ jsx("div", {
283
+ className: "record-agent-tree max-h-80 overflow-auto px-2 py-2",
284
+ "data-graph": true,
285
+ role: "group",
286
+ "aria-label": "Recorded agent hierarchy. Select an agent to read its conversation.",
287
+ children: roots.length ? renderNodes(roots) : /* @__PURE__ */ jsx("p", {
288
+ className: "text-sm text-muted-foreground",
289
+ children: "No agent topology was retained."
290
+ })
291
+ });
292
+ }
293
+ export { Conversation as n, AgentTree as t };
@@ -1,4 +1,5 @@
1
- import { a as fmt, c as modelIdentity, d as recordFlowTrace, f as roleOf, h as utcTime, i as eventSource, m as toolKey, n as Conversation, o as indexRecord, p as textOf, r as eventMatches, s as interval, t as AgentTree, u as ms } from "./AgentTree-Cju8jn51.js";
1
+ import { C as ms, D as toolKey, E as textOf, O as utcTime, T as roleOf, _ as eventSource, b as interval, g as eventMatches, v as fmt, w as recordFlowTrace, x as modelIdentity, y as indexRecord } from "./use-follow-tail-DZFn7QDO.js";
2
+ import { n as Conversation, t as AgentTree } from "./AgentTree-D4d06rfH.js";
2
3
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "@tangle-network/ui/primitives";
3
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
5
  import { createContext, memo, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
@@ -159,13 +159,40 @@ function useSettleTarget(scroller, { target, align, items, keyOf, offsets, origi
159
159
  offsets
160
160
  ]);
161
161
  }
162
+ /** Animate a new followed row once; measurements and reader-state updates keep the same arrival identity. */
163
+ function useArrival(scroller, rows, arrival) {
164
+ const identity = arrival?.identity;
165
+ const prior = useRef({
166
+ identity,
167
+ id: arrival?.id
168
+ });
169
+ useLayoutEffect(() => {
170
+ const animate = prior.current.identity === identity && !!prior.current.id && prior.current.id !== arrival?.id;
171
+ prior.current = {
172
+ identity,
173
+ id: arrival?.id
174
+ };
175
+ if (!animate || !arrival?.key || !arrival.following.current) return;
176
+ const frame = requestAnimationFrame(() => {
177
+ const element = scroller.current, row = rows.current.get(arrival.key)?.firstElementChild;
178
+ if (!element || !row || !arrival.following.current) return;
179
+ const style = getComputedStyle(element), value = style.getPropertyValue("--duration-stream").trim(), duration = parseFloat(value) * (value.endsWith("ms") ? 1 : 1e3);
180
+ if (duration > 0 && !matchMedia("(prefers-reduced-motion: reduce)").matches) row.animate([{ opacity: 0 }, { opacity: 1 }], {
181
+ duration,
182
+ easing: style.getPropertyValue("--ease-expo").trim() || "linear"
183
+ });
184
+ });
185
+ return () => cancelAnimationFrame(frame);
186
+ }, [identity, arrival?.id]);
187
+ }
162
188
  /**
163
189
  * Renders only the rows near the viewport of `scroller`. Row heights are measured as they render; unmeasured
164
190
  * rows use `estimate`. Spacers keep the scroll height, so the browser's scroll anchoring holds the reading place.
165
191
  */
166
- function VirtualList({ items, keyOf, render, scroller, estimate = 96, overscan = 900, target, origin, targetAlign = "start", virtualize = true, keepMounted }) {
192
+ function VirtualList({ items, keyOf, render, scroller, estimate = 96, overscan = 900, target, origin, targetAlign = "start", virtualize = true, keepMounted, arrival }) {
167
193
  const { heights, rows, version, bind } = useMeasuredHeights();
168
194
  const { viewport, originTop } = useViewport(scroller, origin);
195
+ useArrival(scroller, rows, arrival);
169
196
  const offsets = useMemo(() => {
170
197
  const result = new Float64Array(items.length + 1);
171
198
  const guess = typeof estimate === "function" ? estimate : () => estimate;
package/dist/globals.css CHANGED
@@ -3968,6 +3968,18 @@
3968
3968
  .text-\[var\(--surface-warning-text\)\] {
3969
3969
  color: var(--surface-warning-text);
3970
3970
  }
3971
+ .text-\[var\(--syntax-function\,currentColor\)\] {
3972
+ color: var(--syntax-function,currentColor);
3973
+ }
3974
+ .text-\[var\(--syntax-keyword\,currentColor\)\] {
3975
+ color: var(--syntax-keyword,currentColor);
3976
+ }
3977
+ .text-\[var\(--syntax-number\,currentColor\)\] {
3978
+ color: var(--syntax-number,currentColor);
3979
+ }
3980
+ .text-\[var\(--syntax-string\,currentColor\)\] {
3981
+ color: var(--syntax-string,currentColor);
3982
+ }
3971
3983
  .text-\[var\(--text-dim\)\] {
3972
3984
  color: var(--text-dim);
3973
3985
  }
@@ -5991,9 +6003,6 @@
5991
6003
  text-overflow: ellipsis;
5992
6004
  white-space: nowrap;
5993
6005
  }
5994
- .sm\:p-4 {
5995
- padding: calc(var(--spacing) * 4);
5996
- }
5997
6006
  .sm\:p-5 {
5998
6007
  padding: calc(var(--spacing) * 5);
5999
6008
  }
@@ -1,5 +1,116 @@
1
+ import { t as AgentStreamOpener } from "./event-stream-DNEbvx4s.js";
1
2
  import { ReactNode, RefObject } from "react";
3
+ import { ToolCallData } from "@tangle-network/ui/run";
2
4
  import { FlowTrace, formatCompact as fmt, formatSpanDuration as interval, formatUtcTime as utcTime } from "@tangle-network/charts";
5
+ import { AgentTimelineItem, AgentTimelineStepKind } from "@tangle-network/ui/chat/agent-timeline";
6
+ /** One normalized stream row; bounded producer pages also carry opaque native frames. */
7
+ interface AgentStreamRow {
8
+ seq: number;
9
+ at: string;
10
+ stream?: string;
11
+ attempt?: number;
12
+ event: {
13
+ kind: "text" | "reasoning" | "tool-call" | "tool-result" | "user" | "notice" | (string & {});
14
+ text?: string;
15
+ provenance?: string;
16
+ toolCallId?: string;
17
+ toolName?: string;
18
+ args?: unknown;
19
+ result?: unknown;
20
+ isError?: boolean;
21
+ atBasis?: string;
22
+ durationMs?: number;
23
+ costUsd?: number;
24
+ id?: string;
25
+ sealed?: boolean;
26
+ };
27
+ }
28
+ type AgentTranscriptEntry = {
29
+ id: string;
30
+ at: number;
31
+ scope?: string;
32
+ attempt?: number;
33
+ provenance?: string;
34
+ isError?: boolean;
35
+ kind: "message";
36
+ role: "assistant" | "user";
37
+ content: string;
38
+ sealed?: true;
39
+ } | {
40
+ id: string;
41
+ at: number;
42
+ scope?: string;
43
+ attempt?: number;
44
+ provenance?: string;
45
+ isError?: boolean;
46
+ kind: "reasoning";
47
+ text: string;
48
+ } | {
49
+ id: string;
50
+ at: number;
51
+ scope?: string;
52
+ attempt?: number;
53
+ provenance?: string;
54
+ isError?: boolean;
55
+ kind: "tool";
56
+ call: ToolCallData;
57
+ name?: string;
58
+ input?: unknown;
59
+ costUsd?: number;
60
+ } | {
61
+ id: string;
62
+ at: number;
63
+ scope?: string;
64
+ attempt?: number;
65
+ provenance?: string;
66
+ isError?: boolean;
67
+ kind: "notice";
68
+ text: string;
69
+ };
70
+ type ProducerRow = Partial<AgentStreamRow> & Pick<AgentStreamRow, "seq"> & {
71
+ type?: string;
72
+ data?: unknown;
73
+ };
74
+ interface AgentStreamPage {
75
+ generation?: string | number;
76
+ events: ProducerRow[];
77
+ firstSeq?: number | null;
78
+ lastSeq?: number | null;
79
+ hasEarlier?: boolean;
80
+ hasMore?: boolean;
81
+ alive?: boolean;
82
+ reset?: boolean;
83
+ }
84
+ /** The actual producer's bounded history. Core projection is product-owned, never a parser in the SDK. */
85
+ interface AgentStreamHistory {
86
+ url: string;
87
+ alive: boolean;
88
+ projectRows?: (rows: readonly ProducerRow[]) => readonly ProducerRow[];
89
+ }
90
+ interface AgentStreamState {
91
+ generation?: string | number;
92
+ entries: AgentTranscriptEntry[];
93
+ lastSeq: number;
94
+ alive: boolean;
95
+ error?: string;
96
+ history?: {
97
+ loading: boolean;
98
+ earlier: boolean;
99
+ more: boolean;
100
+ reading: boolean;
101
+ beginning: boolean;
102
+ emptyTail: boolean;
103
+ older(): void;
104
+ newer(): void;
105
+ latest(): void;
106
+ beginningPage(): void;
107
+ };
108
+ }
109
+ /** One fold for live entries; attempt/session identity prevents repeated native call IDs from crossing turns. */
110
+ declare function foldAgentRows(entries: AgentTranscriptEntry[], rows: readonly AgentStreamRow[]): AgentTranscriptEntry[];
111
+ declare function foldAgentRows(entries: AgentTranscriptEntry[], rows: readonly ProducerRow[]): AgentTranscriptEntry[];
112
+ /** Same named SSE lifecycle for every consumer; the actual history endpoint owns bounded bootstrap/pages. */
113
+ declare function useAgentStream(url: string | undefined, openEventSource?: AgentStreamOpener, history?: AgentStreamHistory): AgentStreamState;
3
114
  /** Presentation inputs, not a wire schema. Producers validate retained data before rendering it. */
4
115
  interface ConversationNode {
5
116
  id: string;
@@ -189,7 +300,7 @@ declare function withPrompts<T extends ConversationRecord>(record: T): T;
189
300
  * Renders only the rows near the viewport of `scroller`. Row heights are measured as they render; unmeasured
190
301
  * rows use `estimate`. Spacers keep the scroll height, so the browser's scroll anchoring holds the reading place.
191
302
  */
192
- declare function VirtualList<T>({ items, keyOf, render, scroller, estimate, overscan, target, origin, targetAlign, virtualize, keepMounted }: {
303
+ declare function VirtualList<T>({ items, keyOf, render, scroller, estimate, overscan, target, origin, targetAlign, virtualize, keepMounted, arrival }: {
193
304
  items: readonly T[];
194
305
  keyOf: (item: T) => string;
195
306
  render: (item: T, index: number) => ReactNode;
@@ -206,6 +317,13 @@ declare function VirtualList<T>({ items, keyOf, render, scroller, estimate, over
206
317
  virtualize?: boolean;
207
318
  /** Keep one roving tab stop mounted when the reader scrolls away from it. */
208
319
  keepMounted?: string;
320
+ /** Animate a new followed row using the shared Brand motion tokens. */
321
+ arrival?: {
322
+ identity: unknown;
323
+ id?: string;
324
+ key?: string;
325
+ following: RefObject<boolean>;
326
+ };
209
327
  }): import("react").JSX.Element;
210
328
  /** Retained identity and recorded time define the hierarchy; labels never join sessions or invent parents. */
211
329
  declare function AgentTree({ index, actor, cutoff, select }: {
@@ -214,4 +332,19 @@ declare function AgentTree({ index, actor, cutoff, select }: {
214
332
  cutoff: number;
215
333
  select: (id: string) => void;
216
334
  }): import("react").JSX.Element;
217
- export { ConversationProps as a, ToolCall as c, recordFlowTrace as d, ConversationEvent as f, Conversation as i, indexRecord as l, ConversationRecord as m, VirtualList as n, EventFlag as o, ConversationNode as p, withPrompts as r, RecordIndex as s, AgentTree as t, model_d_exports as u };
335
+ declare function ToolStep({ item, previous }: {
336
+ item: Extract<AgentTimelineItem, {
337
+ kind: 'tool';
338
+ }>;
339
+ previous?: AgentTimelineStepKind;
340
+ }): import("react").JSX.Element;
341
+ /** The same prompt control for retained and streamed turns; opening state belongs to the reader. */
342
+ declare function PromptMore({ text, id, opened, setOpen }: {
343
+ text: string;
344
+ id: string;
345
+ opened: ReadonlySet<string>;
346
+ setOpen: (id: string, open: boolean) => void;
347
+ }): import("react").JSX.Element | null;
348
+ /** Live values use the same canonical items and tool presentation as retained conversations. */
349
+ declare function timelineItems(entries: readonly AgentTranscriptEntry[], streaming: boolean, opened?: ReadonlySet<string>): AgentTimelineItem[];
350
+ export { foldAgentRows as C, AgentTranscriptEntry as S, ConversationRecord as _, VirtualList as a, AgentStreamRow as b, ConversationProps as c, ToolCall as d, indexRecord as f, ConversationNode as g, ConversationEvent as h, AgentTree as i, EventFlag as l, recordFlowTrace as m, ToolStep as n, withPrompts as o, model_d_exports as p, timelineItems as r, Conversation as s, PromptMore as t, RecordIndex as u, AgentStreamHistory as v, useAgentStream as w, AgentStreamState as x, AgentStreamPage as y };
@@ -1,8 +1,8 @@
1
1
  import { l as AgentGraphNode, u as AgentGraphProps$1 } from "./AgentGraph-P7l-jZwo.js";
2
2
  import { t as AgentStreamOpener } from "./event-stream-DNEbvx4s.js";
3
- import "./index-gXcuwPOX.js";
3
+ import "./index-Bv2iiZaN.js";
4
4
  import { ReactNode } from "react";
5
- import { ApprovalCard, ApprovalCardProps, ApprovalDiffFile, ApprovalDiffSummary, ApprovalPlanStep, ExpandedToolDetail, ExpandedToolDetailProps, FeedSegment, InlineThinkingItem as InlineThinkingItem$1, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps, RunPhase, RunPhaseList, RunRowShell, RunRowShellProps, RunRowStatus, RunRowStatusDot, RunStatus, RunStatusIcon as RunStatusIcon$1, RunStatusIconProps, StatusMeta, TaskList, TaskListItem, TaskListProps, TaskStatus, ToolCallData, ToolCallData as ToolCallData$1, ToolCallFeed, ToolCallFeedProps, ToolCallStatus, ToolCallType, isOpen, parseToolEvent, runStatus as runStatus$1, showsTaskPriority, statusMeta as statusMeta$1, taskProgress, taskStatus } from "@tangle-network/ui/run";
5
+ import { ApprovalCard, ApprovalCardProps, ApprovalDiffFile, ApprovalDiffSummary, ApprovalPlanStep, ExpandedToolDetail, ExpandedToolDetailProps, FeedSegment, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps, RunPhase, RunPhaseList, RunRowShell, RunRowShellProps, RunRowStatus, RunRowStatusDot, RunStatus, RunStatusIcon as RunStatusIcon$1, RunStatusIconProps, StatusMeta, TaskList, TaskListItem, TaskListProps, TaskStatus, ToolCallData as ToolCallData$1, ToolCallFeed, ToolCallFeedProps, ToolCallStatus, ToolCallType, isOpen, parseToolEvent, runStatus as runStatus$1, showsTaskPriority, statusMeta as statusMeta$1, taskProgress, taskStatus } from "@tangle-network/ui/run";
6
6
  import { FlowWaterfallProps } from "@tangle-network/charts/react";
7
7
  import { LatestEvents, LatestEventsProps } from "@tangle-network/charts/react/agent-graph";
8
8
  import { FlowMarker, FlowSpan, FlowTrace } from "@tangle-network/charts";
@@ -15,64 +15,6 @@ type TraceWaterfallProps = FlowWaterfallProps;
15
15
  * `traces convert` contract. Import `@tangle-network/charts/react/flow-waterfall.css` once.
16
16
  */
17
17
  declare function TraceWaterfall({ renderIcon, ...props }: TraceWaterfallProps): import("react").JSX.Element;
18
- /** One row of an agent's message stream (tangle-tools `/api/discovery/runs/<run>/agents/<agent>/stream`). */
19
- interface AgentStreamRow {
20
- seq: number;
21
- /** ISO time the event happened. */
22
- at: string;
23
- stream?: string;
24
- attempt?: number;
25
- event: {
26
- kind: "text" | "reasoning" | "tool-call" | "tool-result" | "user" | "notice" | (string & {});
27
- text?: string;
28
- /** A notice's origin: the agent's `delegated-input`, its `recorded-result`, or a capture fact such as `capture-unavailable`. */
29
- provenance?: string;
30
- toolCallId?: string;
31
- toolName?: string;
32
- args?: unknown;
33
- result?: unknown;
34
- };
35
- }
36
- /** A transcript entry with the time it began, so a time cursor can show the conversation as of any moment. */
37
- type AgentTranscriptEntry = {
38
- id: string;
39
- at: number;
40
- kind: "message";
41
- role: "assistant" | "user";
42
- content: string;
43
- sealed?: true;
44
- } | {
45
- id: string;
46
- at: number;
47
- kind: "reasoning";
48
- text: string;
49
- } | {
50
- id: string;
51
- at: number;
52
- kind: "tool";
53
- call: ToolCallData;
54
- } | {
55
- id: string;
56
- at: number;
57
- kind: "notice";
58
- text: string;
59
- };
60
- interface AgentStreamState {
61
- generation?: string | number;
62
- entries: AgentTranscriptEntry[];
63
- lastSeq: number;
64
- /** The agent is still producing output. */
65
- alive: boolean;
66
- /** Why the stream shows nothing new; a reconnect that delivers rows clears it. */
67
- error?: string;
68
- }
69
- /** Fold stream rows into transcript entries: text of one turn merges, a tool call becomes a row its result completes. */
70
- declare function foldAgentRows(entries: AgentTranscriptEntry[], rows: readonly AgentStreamRow[]): AgentTranscriptEntry[];
71
- /**
72
- * Follow one agent's message stream over Server-Sent Events: `rows`, `reset` and `end` events, reconnecting with
73
- * `?after=<lastSeq>` when the server closes the connection, so a long turn streams without gaps or repeats.
74
- */
75
- declare function useAgentStream(url: string | undefined, openEventSource?: AgentStreamOpener): AgentStreamState;
76
18
  /** A moment on the run's event track: a release, a check verdict, a spawn, a crash or a steer. */
77
19
  interface TraceEvent extends FlowMarker {}
78
20
  interface TraceWorkspaceProps extends Omit<TraceWaterfallProps, "trace" | "markers" | "onSelect" | "onTimeChange" | "selectedId" | "onDeselect" | "graph" | "latestEvents"> {
@@ -114,4 +56,4 @@ interface TraceWorkspaceProps extends Omit<TraceWaterfallProps, "trace" | "marke
114
56
  * every panel shows what was true at the cursor, and the conversation shows what had been said by then.
115
57
  */
116
58
  declare function TraceWorkspace({ trace, streamUrl, openEventSource, events, onTimeChange, files, scoreboard, selectedAgentId, conversationOpen, onSelectAgent, agentCards, agentGraph, className, ...waterfall }: TraceWorkspaceProps): import("react").JSX.Element;
117
- export { TraceWaterfallProps as $, ToolCallData$1 as A, taskProgress as B, RunStatusIcon$1 as C, TaskListItem as D, TaskList as E, isOpen as F, TraceWorkspace as G, LatestEvents as H, parseToolEvent as I, AgentStreamState as J, TraceWorkspaceProps as K, runStatus$1 as L, ToolCallFeedProps as M, ToolCallStatus as N, TaskListProps as O, ToolCallType as P, TraceWaterfall as Q, showsTaskPriority as R, RunStatus as S, StatusMeta as T, LatestEventsProps as U, taskStatus as V, TraceEvent as W, foldAgentRows as X, AgentTranscriptEntry as Y, useAgentStream as Z, RunPhaseList as _, ApprovalPlanStep as a, RunRowStatus as b, FeedSegment as c, InlineToolItem as d, traceSpanMark as et, InlineToolItemProps as f, RunPhase as g, RunGroupProps as h, ApprovalDiffSummary as i, ToolCallFeed as j, TaskStatus as k, InlineThinkingItem$1 as l, RunGroup as m, ApprovalCardProps as n, ExpandedToolDetail as o, LiveDuration as p, AgentStreamRow as q, ApprovalDiffFile as r, ExpandedToolDetailProps as s, ApprovalCard as t, InlineThinkingItemProps as u, RunRowShell as v, RunStatusIconProps as w, RunRowStatusDot as x, RunRowShellProps as y, statusMeta$1 as z };
59
+ export { ToolCallData$1 as A, taskProgress as B, RunStatusIcon$1 as C, TaskListItem as D, TaskList as E, isOpen as F, TraceWorkspace as G, LatestEvents as H, parseToolEvent as I, TraceWaterfallProps as J, TraceWorkspaceProps as K, runStatus$1 as L, ToolCallFeedProps as M, ToolCallStatus as N, TaskListProps as O, ToolCallType as P, showsTaskPriority as R, RunStatus as S, StatusMeta as T, LatestEventsProps as U, taskStatus as V, TraceEvent as W, traceSpanMark as Y, RunPhaseList as _, ApprovalPlanStep as a, RunRowStatus as b, FeedSegment as c, InlineToolItem as d, InlineToolItemProps as f, RunPhase as g, RunGroupProps as h, ApprovalDiffSummary as i, ToolCallFeed as j, TaskStatus as k, InlineThinkingItem as l, RunGroup as m, ApprovalCardProps as n, ExpandedToolDetail as o, LiveDuration as p, TraceWaterfall as q, ApprovalDiffFile as r, ExpandedToolDetailProps as s, ApprovalCard as t, InlineThinkingItemProps as u, RunRowShell as v, RunStatusIconProps as w, RunRowStatusDot as x, RunRowShellProps as y, statusMeta$1 as z };