@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.
- package/README.md +0 -47
- package/dist/AgentTree-D4d06rfH.js +293 -0
- package/dist/{ResearchReport-vqefUbVp.js → ResearchReport-D1TFmldm.js} +2 -1
- package/dist/{VirtualList-CMynqkkh.js → VirtualList-DcSGbEtA.js} +28 -1
- package/dist/globals.css +12 -3
- package/dist/{index-gXcuwPOX.d.ts → index-Bv2iiZaN.d.ts} +135 -2
- package/dist/{index-YU_b9xCK.d.ts → index-CHA38az4.d.ts} +3 -61
- package/dist/{index-CQGMhSg0.d.ts → index-Z6RCpkmT.d.ts} +16 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +10 -10
- package/dist/record/ResearchReport.js +1 -1
- package/dist/{record-B_4XWfUz.js → record-C-EUpK-n.js} +3 -2
- package/dist/record.js +1 -1
- package/dist/report-viewer.js +80 -80
- package/dist/run/live.js +2 -2
- package/dist/run/record.d.ts +2 -2
- package/dist/run/record.js +5 -4
- package/dist/run.d.ts +3 -3
- package/dist/run.js +6 -5
- package/dist/runtime-viewer/{AgentGraphCanvas-DGq7_u8--WPFPgd72.js → AgentGraphCanvas-DGq7_u8--C6yDfIUT.js} +67 -67
- package/dist/runtime-viewer/{latest-events-CRq9sGkK-YFXUh9qO.js → latest-events-CRq9sGkK-CfoBl1dP.js} +215 -209
- package/dist/styles.css +12 -3
- package/dist/{trace-workspace-BiJbv9WQ.js → trace-workspace-B634-EcA.js} +287 -201
- package/dist/{tree-data-9D309DqD.js → tree-data-rg5voiY8.js} +1 -1
- package/dist/{AgentTree-Cju8jn51.js → use-follow-tail-DZFn7QDO.js} +144 -385
- package/dist/vault-tree.js +1 -1
- package/dist/{workbench-Z9wbei53.js → workbench-DWQ0pm9w.js} +311 -2
- package/dist/workbench.d.ts +2 -2
- package/dist/workbench.js +2 -2
- package/dist/{workspace-BrS_rHWN.js → workspace-CJD7oOEa.js} +1 -1
- package/dist/{workspace-files-pane-Di_rn4FL.js → workspace-files-pane-D24G7H_Z.js} +1 -1
- package/dist/workspace-viewer.js +9970 -9624
- package/dist/workspace.js +2 -2
- package/package.json +5 -8
- package/dist/connections-DymjzHe_.js +0 -179
- package/dist/connections.d.ts +0 -2
- package/dist/connections.js +0 -1
- package/dist/index-DLX35YAa.d.ts +0 -59
- 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 {
|
|
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
|
-
|
|
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-
|
|
3
|
+
import "./index-Bv2iiZaN.js";
|
|
4
4
|
import { ReactNode } from "react";
|
|
5
|
-
import { ApprovalCard, ApprovalCardProps, ApprovalDiffFile, ApprovalDiffSummary, ApprovalPlanStep, ExpandedToolDetail, ExpandedToolDetailProps, FeedSegment, InlineThinkingItem
|
|
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 {
|
|
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 };
|