@tangle-network/sandbox-ui 0.113.7 → 0.115.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 +40 -0
- package/dist/WorkflowGraph-B-NsdYz_.js +1961 -0
- package/dist/assets.d.ts +214 -206
- package/dist/assets.js +1057 -908
- package/dist/auth-CWe17Hly.js +37 -0
- package/dist/auth.d.ts +2 -30
- package/dist/auth.js +2 -15
- package/dist/chat-BOvFAe6p.js +903 -0
- package/dist/chat.d.ts +2 -230
- package/dist/chat.js +2 -34
- package/dist/connectors.d.ts +52 -49
- package/dist/connectors.js +595 -643
- package/dist/dashboard-CZ3VgNPR.js +4969 -0
- package/dist/dashboard.d.ts +4 -1297
- package/dist/dashboard.js +4 -152
- package/dist/editor.d.ts +2 -1
- package/dist/editor.js +2 -31
- package/dist/files.d.ts +2 -6
- package/dist/files.js +2 -33
- package/dist/globals.css +75 -3
- package/dist/hooks.d.ts +146 -148
- package/dist/hooks.js +222 -55
- package/dist/index-CKRaj1pM.d.ts +776 -0
- package/dist/index-CXX5IpH2.d.ts +2 -0
- package/dist/index-CesPlOyx.d.ts +367 -0
- package/dist/index-DPePq7c8.d.ts +1340 -0
- package/dist/index-DiyLLZuJ.d.ts +230 -0
- package/dist/index-DvmZLMT1.d.ts +30 -0
- package/dist/index-PRM636Ex.d.ts +86 -0
- package/dist/index.d.ts +21 -33
- package/dist/index.js +21 -701
- package/dist/info-panel-C9cqDzOL.js +594 -0
- package/dist/integrations.d.ts +133 -141
- package/dist/integrations.js +491 -673
- package/dist/markdown.d.ts +2 -1
- package/dist/markdown.js +2 -13
- package/dist/model-I-NIxeM-.js +1250 -0
- package/dist/model-brand-B9A4H9pJ.d.ts +98 -0
- package/dist/model-brand-hMyDTUqB.js +624 -0
- package/dist/motion-DkyBRJb9.js +37 -0
- package/dist/openui.d.ts +2 -1
- package/dist/openui.js +2 -7
- package/dist/pages.d.ts +307 -300
- package/dist/pages.js +4404 -4181
- package/dist/primitives-Dh1vG6J3.js +113 -0
- package/dist/primitives.d.ts +2 -74
- package/dist/primitives.js +2 -204
- package/dist/provider-logo-DjqmConF.js +311 -0
- package/dist/run.d.ts +2 -1
- package/dist/run.js +2 -23
- package/dist/sdk-hooks.d.ts +3 -4
- package/dist/sdk-hooks.js +3 -31
- package/dist/sidecar-DgH_vQ6m.d.ts +9 -0
- package/dist/stores.d.ts +14 -15
- package/dist/stores.js +13 -81
- package/dist/styles.css +75 -3
- package/dist/template-card-L-4gmjx2.d.ts +90 -0
- package/dist/terminal-CN9ZgHRt.js +197 -0
- package/dist/terminal.d.ts +60 -60
- package/dist/terminal.js +2 -10
- package/dist/tokens.css +51 -0
- package/dist/types.d.ts +4 -11
- package/dist/types.js +0 -1
- package/dist/use-pty-session-C8cE0adT.js +669 -0
- package/dist/use-sidecar-auth-CCTJcrq7.d.ts +145 -0
- package/dist/use-sidecar-auth-DkdcufOz.js +742 -0
- package/dist/utils-DzWbktWG.js +2 -0
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +2 -37
- package/dist/workbench-ztU44Jt2.js +8455 -0
- package/dist/workbench.d.ts +2 -310
- package/dist/workbench.js +2 -44
- package/dist/workflows.d.ts +341 -339
- package/dist/workflows.js +74 -109
- package/dist/workspace-B6AtI6ld.js +2510 -0
- package/dist/workspace.d.ts +2 -761
- package/dist/workspace.js +2 -51
- package/package.json +46 -40
- package/dist/WorkflowGraph-2QOORYIR.js +0 -1722
- package/dist/chunk-2BUPSB7O.js +0 -0
- package/dist/chunk-3KBNPQQF.js +0 -2697
- package/dist/chunk-3LIXVEEE.js +0 -279
- package/dist/chunk-72UF2H5H.js +0 -707
- package/dist/chunk-AI25G5MX.js +0 -944
- package/dist/chunk-ANSC5AFC.js +0 -314
- package/dist/chunk-AZ3AWMTM.js +0 -8
- package/dist/chunk-BGVEHENC.js +0 -10
- package/dist/chunk-CZ7CSFO4.js +0 -39
- package/dist/chunk-DYXXYGS4.js +0 -4700
- package/dist/chunk-EI44GEQ5.js +0 -6
- package/dist/chunk-IACRDXER.js +0 -219
- package/dist/chunk-IOB2PW5Z.js +0 -55
- package/dist/chunk-M4K7ZGR2.js +0 -24
- package/dist/chunk-MYZAW4RX.js +0 -665
- package/dist/chunk-OMUIPJQZ.js +0 -555
- package/dist/chunk-RQZRXKMR.js +0 -398
- package/dist/chunk-SOKKTB7W.js +0 -230
- package/dist/chunk-TADGS4Q7.js +0 -1010
- package/dist/chunk-UCQNOUN3.js +0 -7964
- package/dist/chunk-ULPLRBMF.js +0 -34
- package/dist/chunk-WID73FPH.js +0 -89
- package/dist/chunk-X3YXP7FG.js +0 -38
- package/dist/logo-DiAjq759.d.ts +0 -15
- package/dist/model-brand-BNnaUKKZ.d.ts +0 -99
- package/dist/ports-list-CDIvTNnX.d.ts +0 -17
- package/dist/sdk-hooks-Djkjq4cp.d.ts +0 -145
- package/dist/template-card-Bn-Bw5xC.d.ts +0 -87
package/dist/chunk-TADGS4Q7.js
DELETED
|
@@ -1,1010 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
cn
|
|
3
|
-
} from "./chunk-EI44GEQ5.js";
|
|
4
|
-
|
|
5
|
-
// src/chat/index.ts
|
|
6
|
-
import {
|
|
7
|
-
AgentTimeline,
|
|
8
|
-
ChatContainer,
|
|
9
|
-
ChatMessage as ChatMessage2,
|
|
10
|
-
MessageList,
|
|
11
|
-
ThinkingIndicator,
|
|
12
|
-
UserMessage
|
|
13
|
-
} from "@tangle-network/ui/chat";
|
|
14
|
-
|
|
15
|
-
// src/chat/reasoning-level-picker.tsx
|
|
16
|
-
import {
|
|
17
|
-
reasoningLadder
|
|
18
|
-
} from "@tangle-network/agent-interface";
|
|
19
|
-
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
|
20
|
-
import { Brain, ChevronDown, Sparkles } from "lucide-react";
|
|
21
|
-
import { focusRing } from "@tangle-network/ui/utils";
|
|
22
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
23
|
-
var REASONING_LADDER = reasoningLadder;
|
|
24
|
-
function ReasoningGlyph({ level }) {
|
|
25
|
-
if (level === "auto") {
|
|
26
|
-
return /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 text-muted-foreground" });
|
|
27
|
-
}
|
|
28
|
-
const BARS = 4;
|
|
29
|
-
const rank = REASONING_LADDER.indexOf(level) + 1;
|
|
30
|
-
const filled = Math.max(1, Math.ceil(rank / REASONING_LADDER.length * BARS));
|
|
31
|
-
return /* @__PURE__ */ jsx(
|
|
32
|
-
"span",
|
|
33
|
-
{
|
|
34
|
-
"aria-hidden": true,
|
|
35
|
-
className: "inline-flex h-3.5 items-end gap-px",
|
|
36
|
-
style: { width: 16 },
|
|
37
|
-
children: Array.from({ length: BARS }).map((_, i) => /* @__PURE__ */ jsx(
|
|
38
|
-
"span",
|
|
39
|
-
{
|
|
40
|
-
className: cn(
|
|
41
|
-
"w-1 rounded-[1px]",
|
|
42
|
-
i < filled ? "bg-foreground" : "bg-border"
|
|
43
|
-
),
|
|
44
|
-
style: { height: `${35 + i * 22}%` }
|
|
45
|
-
},
|
|
46
|
-
i
|
|
47
|
-
))
|
|
48
|
-
}
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
var DEFAULT_REASONING_LEVEL_OPTIONS = [
|
|
52
|
-
{ value: "auto", label: "Auto", description: "Let the agent pick the right depth." },
|
|
53
|
-
{ value: "none", label: "None", description: "No extended thinking \u2014 fastest, cheapest." },
|
|
54
|
-
{ value: "minimal", label: "Minimal", description: "Thinking on, lowest budget." },
|
|
55
|
-
{ value: "low", label: "Low", description: "Fast, direct answers." },
|
|
56
|
-
{ value: "medium", label: "Medium", description: "Inspect context before acting." },
|
|
57
|
-
{ value: "high", label: "High", description: "Deeper planning and edge-case checks." },
|
|
58
|
-
{ value: "xhigh", label: "Extra High", description: "Extended reasoning for the hardest problems." },
|
|
59
|
-
{ value: "ultracode", label: "Ultracode", description: "Maximum extended thinking." }
|
|
60
|
-
];
|
|
61
|
-
var HARNESS_REASONING_OPTIONS = {
|
|
62
|
-
"kimi-code": [
|
|
63
|
-
{ value: "auto", label: "Auto", description: "Use Kimi's default." },
|
|
64
|
-
{ value: "none", label: "No thinking", description: "Kimi answers directly \u2014 thinking off." },
|
|
65
|
-
{ value: "high", label: "Thinking", description: "Kimi reasons before answering." }
|
|
66
|
-
]
|
|
67
|
-
};
|
|
68
|
-
function ReasoningLevelPicker({
|
|
69
|
-
value,
|
|
70
|
-
onChange,
|
|
71
|
-
disabled,
|
|
72
|
-
className,
|
|
73
|
-
triggerClassName,
|
|
74
|
-
options = DEFAULT_REASONING_LEVEL_OPTIONS,
|
|
75
|
-
available,
|
|
76
|
-
side,
|
|
77
|
-
avoidCollisions
|
|
78
|
-
}) {
|
|
79
|
-
const shown = available ? options.filter((o) => o.value === "auto" || available.includes(o.value)) : options;
|
|
80
|
-
const selected = shown.find((option) => option.value === value);
|
|
81
|
-
const label = selected?.label ?? "Auto";
|
|
82
|
-
return /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
|
|
83
|
-
/* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
84
|
-
"button",
|
|
85
|
-
{
|
|
86
|
-
type: "button",
|
|
87
|
-
disabled,
|
|
88
|
-
className: cn(
|
|
89
|
-
"inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5",
|
|
90
|
-
"text-xs font-medium text-foreground shadow-sm transition-colors",
|
|
91
|
-
"hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high",
|
|
92
|
-
focusRing,
|
|
93
|
-
"data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
|
|
94
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
95
|
-
className,
|
|
96
|
-
triggerClassName
|
|
97
|
-
),
|
|
98
|
-
"aria-label": "Reasoning level",
|
|
99
|
-
children: [
|
|
100
|
-
/* @__PURE__ */ jsx(Brain, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
101
|
-
/* @__PURE__ */ jsx("span", { children: label }),
|
|
102
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
|
|
103
|
-
]
|
|
104
|
-
}
|
|
105
|
-
) }),
|
|
106
|
-
/* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx(
|
|
107
|
-
DropdownMenu.Content,
|
|
108
|
-
{
|
|
109
|
-
side,
|
|
110
|
-
align: "start",
|
|
111
|
-
avoidCollisions,
|
|
112
|
-
collisionPadding: 24,
|
|
113
|
-
sideOffset: 6,
|
|
114
|
-
className: cn(
|
|
115
|
-
// Cap to the viewport space on the open side and scroll, so the menu
|
|
116
|
-
// pinned downward (floating composer) never runs off the bottom edge.
|
|
117
|
-
"z-50 w-64 max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-1",
|
|
118
|
-
"shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
|
|
119
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
120
|
-
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
121
|
-
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
|
|
122
|
-
),
|
|
123
|
-
children: shown.map((option) => /* @__PURE__ */ jsxs(
|
|
124
|
-
DropdownMenu.Item,
|
|
125
|
-
{
|
|
126
|
-
onSelect: () => onChange(option.value),
|
|
127
|
-
className: cn(
|
|
128
|
-
"flex cursor-pointer items-start gap-2.5 rounded-md px-2.5 py-2 outline-none",
|
|
129
|
-
"transition-colors hover:bg-accent/40 focus:bg-accent/40",
|
|
130
|
-
option.value === value && "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]"
|
|
131
|
-
),
|
|
132
|
-
children: [
|
|
133
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5 flex w-3.5 shrink-0 justify-center", children: /* @__PURE__ */ jsx(ReasoningGlyph, { level: option.value }) }),
|
|
134
|
-
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
135
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: option.label }),
|
|
136
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: option.description })
|
|
137
|
-
] })
|
|
138
|
-
]
|
|
139
|
-
},
|
|
140
|
-
option.value
|
|
141
|
-
))
|
|
142
|
-
}
|
|
143
|
-
) })
|
|
144
|
-
] });
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// src/chat/agent-profile-picker.tsx
|
|
148
|
-
import { Bot, Check, ChevronDown as ChevronDown2, Pencil, Plus as Plus2, Trash2 } from "lucide-react";
|
|
149
|
-
import * as React4 from "react";
|
|
150
|
-
|
|
151
|
-
// src/lib/use-click-outside.ts
|
|
152
|
-
import * as React from "react";
|
|
153
|
-
function useClickOutside(onOutside, extras) {
|
|
154
|
-
const ref = React.useRef(null);
|
|
155
|
-
const extrasRef = React.useRef(extras);
|
|
156
|
-
extrasRef.current = extras;
|
|
157
|
-
React.useEffect(() => {
|
|
158
|
-
function handler(event) {
|
|
159
|
-
const target = event.target;
|
|
160
|
-
if (!ref.current || ref.current.contains(target)) return;
|
|
161
|
-
if (extrasRef.current?.some((extra) => extra.current?.contains(target))) return;
|
|
162
|
-
onOutside();
|
|
163
|
-
}
|
|
164
|
-
document.addEventListener("mousedown", handler);
|
|
165
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
166
|
-
}, [onOutside]);
|
|
167
|
-
return ref;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// src/chat/anchored-popover.tsx
|
|
171
|
-
import * as React2 from "react";
|
|
172
|
-
import { createPortal } from "react-dom";
|
|
173
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
174
|
-
var AnchoredPopover = React2.forwardRef(
|
|
175
|
-
function AnchoredPopover2({ anchorRef, side, children, className, style, ...rest }, forwardedRef) {
|
|
176
|
-
const innerRef = React2.useRef(null);
|
|
177
|
-
const setRefs = (node) => {
|
|
178
|
-
innerRef.current = node;
|
|
179
|
-
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
180
|
-
else if (forwardedRef) forwardedRef.current = node;
|
|
181
|
-
};
|
|
182
|
-
const [position, setPosition] = React2.useState(null);
|
|
183
|
-
const place = React2.useCallback(() => {
|
|
184
|
-
const anchor = anchorRef.current;
|
|
185
|
-
if (!anchor) return;
|
|
186
|
-
const rect = anchor.getBoundingClientRect();
|
|
187
|
-
const width = innerRef.current?.offsetWidth ?? 0;
|
|
188
|
-
const margin = 8;
|
|
189
|
-
const left = Math.max(
|
|
190
|
-
margin,
|
|
191
|
-
Math.min(rect.left, window.innerWidth - width - margin)
|
|
192
|
-
);
|
|
193
|
-
setPosition(
|
|
194
|
-
side === "top" ? { left, bottom: window.innerHeight - rect.top + margin } : { left, top: rect.bottom + margin }
|
|
195
|
-
);
|
|
196
|
-
}, [anchorRef, side]);
|
|
197
|
-
React2.useLayoutEffect(() => {
|
|
198
|
-
place();
|
|
199
|
-
window.addEventListener("resize", place);
|
|
200
|
-
window.addEventListener("scroll", place, true);
|
|
201
|
-
return () => {
|
|
202
|
-
window.removeEventListener("resize", place);
|
|
203
|
-
window.removeEventListener("scroll", place, true);
|
|
204
|
-
};
|
|
205
|
-
}, [place]);
|
|
206
|
-
return createPortal(
|
|
207
|
-
/* @__PURE__ */ jsx2(
|
|
208
|
-
"div",
|
|
209
|
-
{
|
|
210
|
-
...rest,
|
|
211
|
-
ref: setRefs,
|
|
212
|
-
className: cn("fixed z-50", className),
|
|
213
|
-
style: {
|
|
214
|
-
...style,
|
|
215
|
-
pointerEvents: "auto",
|
|
216
|
-
...position ?? { visibility: "hidden", left: 0, top: 0 }
|
|
217
|
-
},
|
|
218
|
-
children
|
|
219
|
-
}
|
|
220
|
-
),
|
|
221
|
-
document.body
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
// src/chat/informative-lock.tsx
|
|
227
|
-
import { Lock, Plus } from "lucide-react";
|
|
228
|
-
import * as React3 from "react";
|
|
229
|
-
import { focusRing as focusRing2 } from "@tangle-network/ui/utils";
|
|
230
|
-
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
231
|
-
function InformativeLock({
|
|
232
|
-
children,
|
|
233
|
-
ariaLabel,
|
|
234
|
-
lockTitle,
|
|
235
|
-
lockBody,
|
|
236
|
-
newChatLabel,
|
|
237
|
-
onNewChat,
|
|
238
|
-
className,
|
|
239
|
-
triggerClassName,
|
|
240
|
-
popoverClassName,
|
|
241
|
-
side = "top"
|
|
242
|
-
}) {
|
|
243
|
-
const [open, setOpen] = React3.useState(false);
|
|
244
|
-
const closeTimer = React3.useRef(null);
|
|
245
|
-
const popoverRef = React3.useRef(null);
|
|
246
|
-
const ref = useClickOutside(() => setOpen(false), [popoverRef]);
|
|
247
|
-
const cancelClose = React3.useCallback(() => {
|
|
248
|
-
if (closeTimer.current) {
|
|
249
|
-
clearTimeout(closeTimer.current);
|
|
250
|
-
closeTimer.current = null;
|
|
251
|
-
}
|
|
252
|
-
}, []);
|
|
253
|
-
const scheduleClose = React3.useCallback(() => {
|
|
254
|
-
cancelClose();
|
|
255
|
-
closeTimer.current = setTimeout(() => setOpen(false), 150);
|
|
256
|
-
}, [cancelClose]);
|
|
257
|
-
React3.useEffect(() => cancelClose, [cancelClose]);
|
|
258
|
-
return /* @__PURE__ */ jsxs2(
|
|
259
|
-
"div",
|
|
260
|
-
{
|
|
261
|
-
ref,
|
|
262
|
-
className: cn("relative inline-flex", className),
|
|
263
|
-
onMouseEnter: () => {
|
|
264
|
-
cancelClose();
|
|
265
|
-
setOpen(true);
|
|
266
|
-
},
|
|
267
|
-
onMouseLeave: scheduleClose,
|
|
268
|
-
onKeyDown: (event) => {
|
|
269
|
-
if (event.key === "Escape") setOpen(false);
|
|
270
|
-
},
|
|
271
|
-
children: [
|
|
272
|
-
/* @__PURE__ */ jsx3(
|
|
273
|
-
"button",
|
|
274
|
-
{
|
|
275
|
-
type: "button",
|
|
276
|
-
onClick: () => setOpen(true),
|
|
277
|
-
"data-state": open ? "open" : "closed",
|
|
278
|
-
"aria-label": ariaLabel,
|
|
279
|
-
"aria-haspopup": "dialog",
|
|
280
|
-
"aria-expanded": open,
|
|
281
|
-
className: cn(
|
|
282
|
-
"inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5 opacity-60",
|
|
283
|
-
"text-xs font-medium text-foreground shadow-sm transition-colors",
|
|
284
|
-
"hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high",
|
|
285
|
-
focusRing2,
|
|
286
|
-
"data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
|
|
287
|
-
triggerClassName
|
|
288
|
-
),
|
|
289
|
-
children
|
|
290
|
-
}
|
|
291
|
-
),
|
|
292
|
-
open && /* @__PURE__ */ jsxs2(
|
|
293
|
-
AnchoredPopover,
|
|
294
|
-
{
|
|
295
|
-
ref: popoverRef,
|
|
296
|
-
anchorRef: ref,
|
|
297
|
-
side,
|
|
298
|
-
role: "dialog",
|
|
299
|
-
onMouseEnter: () => {
|
|
300
|
-
cancelClose();
|
|
301
|
-
setOpen(true);
|
|
302
|
-
},
|
|
303
|
-
onMouseLeave: scheduleClose,
|
|
304
|
-
className: cn(
|
|
305
|
-
"w-72 rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-3",
|
|
306
|
-
"shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
|
|
307
|
-
popoverClassName
|
|
308
|
-
),
|
|
309
|
-
children: [
|
|
310
|
-
/* @__PURE__ */ jsxs2("p", { className: "flex items-start gap-2 text-sm font-medium text-foreground", children: [
|
|
311
|
-
/* @__PURE__ */ jsx3(Lock, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
312
|
-
/* @__PURE__ */ jsx3("span", { children: lockTitle })
|
|
313
|
-
] }),
|
|
314
|
-
/* @__PURE__ */ jsx3("p", { className: "mt-1.5 text-xs text-muted-foreground", children: lockBody }),
|
|
315
|
-
/* @__PURE__ */ jsxs2(
|
|
316
|
-
"button",
|
|
317
|
-
{
|
|
318
|
-
type: "button",
|
|
319
|
-
onClick: () => {
|
|
320
|
-
setOpen(false);
|
|
321
|
-
onNewChat();
|
|
322
|
-
},
|
|
323
|
-
className: cn(
|
|
324
|
-
"mt-3 flex w-full items-center justify-center gap-1.5 rounded-md px-2.5 py-2 text-sm font-medium text-foreground",
|
|
325
|
-
"bg-primary/10 ring-1 ring-inset ring-primary/25 transition-colors",
|
|
326
|
-
"hover:bg-primary/15",
|
|
327
|
-
focusRing2
|
|
328
|
-
),
|
|
329
|
-
children: [
|
|
330
|
-
/* @__PURE__ */ jsx3(Plus, { className: "h-4 w-4" }),
|
|
331
|
-
newChatLabel
|
|
332
|
-
]
|
|
333
|
-
}
|
|
334
|
-
)
|
|
335
|
-
]
|
|
336
|
-
}
|
|
337
|
-
)
|
|
338
|
-
]
|
|
339
|
-
}
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// src/chat/agent-profile-picker.tsx
|
|
344
|
-
import { focusField, focusRing as focusRing3 } from "@tangle-network/ui/utils";
|
|
345
|
-
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
346
|
-
function profileSubtitle(profile) {
|
|
347
|
-
if (profile.description) return profile.description;
|
|
348
|
-
const count = profile.capabilities?.length ?? 0;
|
|
349
|
-
return `${count} ${count === 1 ? "tool group" : "tool groups"}`;
|
|
350
|
-
}
|
|
351
|
-
function AgentProfilePicker({
|
|
352
|
-
value,
|
|
353
|
-
onChange,
|
|
354
|
-
profiles,
|
|
355
|
-
capabilities,
|
|
356
|
-
onCreate,
|
|
357
|
-
onUpdate,
|
|
358
|
-
onDelete,
|
|
359
|
-
disabled,
|
|
360
|
-
locked,
|
|
361
|
-
lockReason,
|
|
362
|
-
onNewChat,
|
|
363
|
-
className,
|
|
364
|
-
triggerClassName,
|
|
365
|
-
popoverClassName,
|
|
366
|
-
side = "top"
|
|
367
|
-
}) {
|
|
368
|
-
const [open, setOpen] = React4.useState(false);
|
|
369
|
-
const [composing, setComposing] = React4.useState(
|
|
370
|
-
null
|
|
371
|
-
);
|
|
372
|
-
const popoverRef = React4.useRef(null);
|
|
373
|
-
const ref = useClickOutside(() => {
|
|
374
|
-
setOpen(false);
|
|
375
|
-
setComposing(null);
|
|
376
|
-
}, [popoverRef]);
|
|
377
|
-
const canAuthor = Boolean(capabilities && (onCreate || onUpdate));
|
|
378
|
-
const builtins = profiles.filter((profile) => profile.builtin);
|
|
379
|
-
const custom = profiles.filter((profile) => !profile.builtin);
|
|
380
|
-
const selected = profiles.find((profile) => profile.id === value);
|
|
381
|
-
const selectedName = selected?.name ?? value;
|
|
382
|
-
React4.useEffect(() => {
|
|
383
|
-
if (!locked) return;
|
|
384
|
-
setOpen(false);
|
|
385
|
-
setComposing(null);
|
|
386
|
-
}, [locked]);
|
|
387
|
-
function select(id) {
|
|
388
|
-
onChange(id);
|
|
389
|
-
setOpen(false);
|
|
390
|
-
}
|
|
391
|
-
async function save(draft) {
|
|
392
|
-
if (draft.id) await onUpdate?.(draft.id, draft);
|
|
393
|
-
else await onCreate?.(draft);
|
|
394
|
-
setComposing(null);
|
|
395
|
-
}
|
|
396
|
-
if (locked && onNewChat) {
|
|
397
|
-
return /* @__PURE__ */ jsxs3(
|
|
398
|
-
InformativeLock,
|
|
399
|
-
{
|
|
400
|
-
ariaLabel: "Agent profile (locked)",
|
|
401
|
-
lockTitle: `Profile fixed to ${selectedName} for this conversation.`,
|
|
402
|
-
lockBody: "Conversations stay on one profile.",
|
|
403
|
-
newChatLabel: "New chat to switch profile",
|
|
404
|
-
onNewChat,
|
|
405
|
-
className,
|
|
406
|
-
triggerClassName,
|
|
407
|
-
popoverClassName,
|
|
408
|
-
side,
|
|
409
|
-
children: [
|
|
410
|
-
/* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
411
|
-
/* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName })
|
|
412
|
-
]
|
|
413
|
-
}
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
return /* @__PURE__ */ jsxs3("div", { ref, className: cn("relative inline-flex", className), children: [
|
|
417
|
-
/* @__PURE__ */ jsxs3(
|
|
418
|
-
"button",
|
|
419
|
-
{
|
|
420
|
-
type: "button",
|
|
421
|
-
disabled: disabled || locked,
|
|
422
|
-
title: locked ? lockReason : void 0,
|
|
423
|
-
onClick: () => setOpen((v) => !v),
|
|
424
|
-
className: cn(
|
|
425
|
-
"inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5",
|
|
426
|
-
"text-xs font-medium text-foreground shadow-sm transition-colors",
|
|
427
|
-
"hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high",
|
|
428
|
-
focusRing3,
|
|
429
|
-
"data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
|
|
430
|
-
"disabled:cursor-not-allowed disabled:opacity-60",
|
|
431
|
-
triggerClassName
|
|
432
|
-
),
|
|
433
|
-
"data-state": open ? "open" : "closed",
|
|
434
|
-
"aria-label": "Agent profile",
|
|
435
|
-
children: [
|
|
436
|
-
/* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
437
|
-
/* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName }),
|
|
438
|
-
!locked && /* @__PURE__ */ jsx4(ChevronDown2, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
|
|
439
|
-
]
|
|
440
|
-
}
|
|
441
|
-
),
|
|
442
|
-
open && !locked && /* @__PURE__ */ jsx4(
|
|
443
|
-
AnchoredPopover,
|
|
444
|
-
{
|
|
445
|
-
ref: popoverRef,
|
|
446
|
-
anchorRef: ref,
|
|
447
|
-
side,
|
|
448
|
-
className: cn(
|
|
449
|
-
"w-[min(320px,calc(100vw-2rem))] overflow-hidden rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-1",
|
|
450
|
-
"shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
|
|
451
|
-
popoverClassName
|
|
452
|
-
),
|
|
453
|
-
"data-testid": "agent-profile-popover",
|
|
454
|
-
children: composing ? /* @__PURE__ */ jsx4(
|
|
455
|
-
ProfileForm,
|
|
456
|
-
{
|
|
457
|
-
capabilities: capabilities ?? [],
|
|
458
|
-
initial: composing === "new" ? void 0 : composing,
|
|
459
|
-
onCancel: () => setComposing(null),
|
|
460
|
-
onSave: save
|
|
461
|
-
}
|
|
462
|
-
) : /* @__PURE__ */ jsxs3("div", { className: "max-h-[min(60vh,420px)] overflow-y-auto", children: [
|
|
463
|
-
/* @__PURE__ */ jsx4(SectionHeader, { children: "Modes" }),
|
|
464
|
-
builtins.map((profile) => /* @__PURE__ */ jsx4(
|
|
465
|
-
ProfileRow,
|
|
466
|
-
{
|
|
467
|
-
active: profile.id === value,
|
|
468
|
-
title: profile.name,
|
|
469
|
-
subtitle: profileSubtitle(profile),
|
|
470
|
-
onSelect: () => select(profile.id)
|
|
471
|
-
},
|
|
472
|
-
profile.id
|
|
473
|
-
)),
|
|
474
|
-
custom.length > 0 && /* @__PURE__ */ jsx4(SectionHeader, { children: "Your agents" }),
|
|
475
|
-
custom.map((profile) => /* @__PURE__ */ jsx4(
|
|
476
|
-
ProfileRow,
|
|
477
|
-
{
|
|
478
|
-
active: profile.id === value,
|
|
479
|
-
title: profile.name,
|
|
480
|
-
subtitle: profileSubtitle(profile),
|
|
481
|
-
onSelect: () => select(profile.id),
|
|
482
|
-
onEdit: canAuthor ? () => setComposing(profile) : void 0,
|
|
483
|
-
onDelete: onDelete ? () => void onDelete(profile.id) : void 0
|
|
484
|
-
},
|
|
485
|
-
profile.id
|
|
486
|
-
)),
|
|
487
|
-
canAuthor && /* @__PURE__ */ jsxs3(
|
|
488
|
-
"button",
|
|
489
|
-
{
|
|
490
|
-
type: "button",
|
|
491
|
-
onClick: () => setComposing("new"),
|
|
492
|
-
className: "mt-1 flex w-full items-center gap-2 rounded-md px-2.5 py-2 text-left text-sm text-[var(--accent-text)] transition-colors hover:bg-accent/40",
|
|
493
|
-
children: [
|
|
494
|
-
/* @__PURE__ */ jsx4(Plus2, { className: "h-4 w-4" }),
|
|
495
|
-
" New agent\u2026"
|
|
496
|
-
]
|
|
497
|
-
}
|
|
498
|
-
)
|
|
499
|
-
] })
|
|
500
|
-
}
|
|
501
|
-
)
|
|
502
|
-
] });
|
|
503
|
-
}
|
|
504
|
-
function SectionHeader({ children }) {
|
|
505
|
-
return /* @__PURE__ */ jsx4("div", { className: "px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children });
|
|
506
|
-
}
|
|
507
|
-
function ProfileRow({
|
|
508
|
-
active,
|
|
509
|
-
title,
|
|
510
|
-
subtitle,
|
|
511
|
-
onSelect,
|
|
512
|
-
onEdit,
|
|
513
|
-
onDelete
|
|
514
|
-
}) {
|
|
515
|
-
return /* @__PURE__ */ jsxs3(
|
|
516
|
-
"div",
|
|
517
|
-
{
|
|
518
|
-
className: cn(
|
|
519
|
-
"group flex items-center gap-2 rounded-md px-2.5 py-2 text-sm transition-colors",
|
|
520
|
-
active ? "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]" : "hover:bg-accent/40"
|
|
521
|
-
),
|
|
522
|
-
children: [
|
|
523
|
-
/* @__PURE__ */ jsxs3(
|
|
524
|
-
"button",
|
|
525
|
-
{
|
|
526
|
-
type: "button",
|
|
527
|
-
onClick: onSelect,
|
|
528
|
-
className: "flex min-w-0 flex-1 items-center gap-2 text-left",
|
|
529
|
-
children: [
|
|
530
|
-
/* @__PURE__ */ jsx4("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: active && /* @__PURE__ */ jsx4(Check, { className: "h-3.5 w-3.5" }) }),
|
|
531
|
-
/* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
|
|
532
|
-
/* @__PURE__ */ jsx4("span", { className: "block truncate font-medium", children: title }),
|
|
533
|
-
/* @__PURE__ */ jsx4("span", { className: "block truncate text-xs text-muted-foreground", children: subtitle })
|
|
534
|
-
] })
|
|
535
|
-
]
|
|
536
|
-
}
|
|
537
|
-
),
|
|
538
|
-
(onEdit || onDelete) && /* @__PURE__ */ jsxs3("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100", children: [
|
|
539
|
-
onEdit && /* @__PURE__ */ jsx4(
|
|
540
|
-
"button",
|
|
541
|
-
{
|
|
542
|
-
type: "button",
|
|
543
|
-
onClick: onEdit,
|
|
544
|
-
title: "Edit",
|
|
545
|
-
className: "rounded p-1 text-muted-foreground hover:text-foreground",
|
|
546
|
-
children: /* @__PURE__ */ jsx4(Pencil, { className: "h-3 w-3" })
|
|
547
|
-
}
|
|
548
|
-
),
|
|
549
|
-
onDelete && /* @__PURE__ */ jsx4(
|
|
550
|
-
"button",
|
|
551
|
-
{
|
|
552
|
-
type: "button",
|
|
553
|
-
onClick: onDelete,
|
|
554
|
-
title: "Delete",
|
|
555
|
-
className: "rounded p-1 text-muted-foreground hover:text-[var(--status-error,#ff4d6d)]",
|
|
556
|
-
children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3 w-3" })
|
|
557
|
-
}
|
|
558
|
-
)
|
|
559
|
-
] })
|
|
560
|
-
]
|
|
561
|
-
}
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
function ProfileForm({
|
|
565
|
-
capabilities,
|
|
566
|
-
initial,
|
|
567
|
-
onCancel,
|
|
568
|
-
onSave
|
|
569
|
-
}) {
|
|
570
|
-
const [name, setName] = React4.useState(initial?.name ?? "");
|
|
571
|
-
const [caps, setCaps] = React4.useState(
|
|
572
|
-
initial?.capabilities ? [...initial.capabilities] : capabilities.map((c) => c.id)
|
|
573
|
-
);
|
|
574
|
-
const [instructions, setInstructions] = React4.useState(initial?.instructions ?? "");
|
|
575
|
-
const toggle = (id) => setCaps(
|
|
576
|
-
(prev) => prev.includes(id) ? prev.filter((c) => c !== id) : [...prev, id]
|
|
577
|
-
);
|
|
578
|
-
return /* @__PURE__ */ jsxs3("div", { className: "space-y-3 p-2.5", children: [
|
|
579
|
-
/* @__PURE__ */ jsx4(
|
|
580
|
-
"input",
|
|
581
|
-
{
|
|
582
|
-
autoFocus: true,
|
|
583
|
-
value: name,
|
|
584
|
-
onChange: (event) => setName(event.target.value),
|
|
585
|
-
placeholder: "Agent name (e.g. Tax Reviewer)",
|
|
586
|
-
className: `w-full rounded-lg border bg-surface-container px-3 py-2 text-sm ${focusField}`
|
|
587
|
-
}
|
|
588
|
-
),
|
|
589
|
-
capabilities.length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
|
|
590
|
-
/* @__PURE__ */ jsx4("p", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children: "Tools" }),
|
|
591
|
-
/* @__PURE__ */ jsx4("div", { className: "space-y-0.5", children: capabilities.map((cap) => /* @__PURE__ */ jsxs3(
|
|
592
|
-
"label",
|
|
593
|
-
{
|
|
594
|
-
className: "flex cursor-pointer items-start gap-2 rounded-md px-2 py-1.5 hover:bg-accent/40",
|
|
595
|
-
children: [
|
|
596
|
-
/* @__PURE__ */ jsx4(
|
|
597
|
-
"input",
|
|
598
|
-
{
|
|
599
|
-
type: "checkbox",
|
|
600
|
-
checked: caps.includes(cap.id),
|
|
601
|
-
onChange: () => toggle(cap.id),
|
|
602
|
-
className: "mt-0.5"
|
|
603
|
-
}
|
|
604
|
-
),
|
|
605
|
-
/* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
|
|
606
|
-
/* @__PURE__ */ jsx4("span", { className: "block text-sm font-medium", children: cap.label }),
|
|
607
|
-
cap.description && /* @__PURE__ */ jsx4("span", { className: "block text-xs text-muted-foreground", children: cap.description })
|
|
608
|
-
] })
|
|
609
|
-
]
|
|
610
|
-
},
|
|
611
|
-
cap.id
|
|
612
|
-
)) })
|
|
613
|
-
] }),
|
|
614
|
-
/* @__PURE__ */ jsx4(
|
|
615
|
-
"textarea",
|
|
616
|
-
{
|
|
617
|
-
value: instructions,
|
|
618
|
-
onChange: (event) => setInstructions(event.target.value),
|
|
619
|
-
rows: 2,
|
|
620
|
-
placeholder: "Instructions (optional) \u2014 how this agent should behave",
|
|
621
|
-
className: `w-full resize-none rounded-lg border bg-surface-container px-3 py-2 text-sm ${focusField}`
|
|
622
|
-
}
|
|
623
|
-
),
|
|
624
|
-
/* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-2", children: [
|
|
625
|
-
/* @__PURE__ */ jsx4(
|
|
626
|
-
"button",
|
|
627
|
-
{
|
|
628
|
-
type: "button",
|
|
629
|
-
onClick: onCancel,
|
|
630
|
-
className: "rounded-lg px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent/40",
|
|
631
|
-
children: "Cancel"
|
|
632
|
-
}
|
|
633
|
-
),
|
|
634
|
-
/* @__PURE__ */ jsx4(
|
|
635
|
-
"button",
|
|
636
|
-
{
|
|
637
|
-
type: "button",
|
|
638
|
-
disabled: !name.trim(),
|
|
639
|
-
onClick: () => onSave({
|
|
640
|
-
id: initial?.id,
|
|
641
|
-
name: name.trim(),
|
|
642
|
-
capabilities: caps,
|
|
643
|
-
instructions: instructions.trim()
|
|
644
|
-
}),
|
|
645
|
-
className: "rounded-lg bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-40",
|
|
646
|
-
children: "Save agent"
|
|
647
|
-
}
|
|
648
|
-
)
|
|
649
|
-
] })
|
|
650
|
-
] });
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// src/chat/harness-model-compat.ts
|
|
654
|
-
import { modelProvider, snapHarnessToModel } from "@tangle-network/agent-interface";
|
|
655
|
-
|
|
656
|
-
// src/chat/artifact-agent-dock.tsx
|
|
657
|
-
import * as React5 from "react";
|
|
658
|
-
import { Sparkles as Sparkles2, Send, Square, X } from "lucide-react";
|
|
659
|
-
import { ChatMessage } from "@tangle-network/ui/chat";
|
|
660
|
-
import { focusField as focusField2 } from "@tangle-network/ui/utils";
|
|
661
|
-
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
662
|
-
function defaultScopeLabel(scope) {
|
|
663
|
-
if (scope.label) return scope.label;
|
|
664
|
-
switch (scope.kind) {
|
|
665
|
-
case "vault-file":
|
|
666
|
-
return scope.key.split("/").pop() ?? scope.key;
|
|
667
|
-
case "sequence":
|
|
668
|
-
return `Sequence ${scope.key.slice(0, 8)}`;
|
|
669
|
-
case "content":
|
|
670
|
-
return `Content ${scope.key.slice(0, 8)}`;
|
|
671
|
-
case "image":
|
|
672
|
-
return `Image ${scope.key.slice(0, 8)}`;
|
|
673
|
-
case "video":
|
|
674
|
-
return `Video ${scope.key.slice(0, 8)}`;
|
|
675
|
-
case "project":
|
|
676
|
-
return "Project chat";
|
|
677
|
-
case "workspace":
|
|
678
|
-
return "Workspace chat";
|
|
679
|
-
default:
|
|
680
|
-
return scope.key;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
function normalizeRole(role) {
|
|
684
|
-
return role === "assistant" || role === "system" ? role : "user";
|
|
685
|
-
}
|
|
686
|
-
function createDockMessageId() {
|
|
687
|
-
const c = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
|
|
688
|
-
if (c && typeof c.randomUUID === "function") return c.randomUUID();
|
|
689
|
-
if (c && typeof c.getRandomValues === "function") {
|
|
690
|
-
const b = c.getRandomValues(new Uint8Array(16));
|
|
691
|
-
b[6] = b[6] & 15 | 64;
|
|
692
|
-
b[8] = b[8] & 63 | 128;
|
|
693
|
-
const h = Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
694
|
-
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
|
|
695
|
-
}
|
|
696
|
-
throw new Error("crypto.getRandomValues is required for message ids");
|
|
697
|
-
}
|
|
698
|
-
function ArtifactAgentDock({
|
|
699
|
-
scope,
|
|
700
|
-
open,
|
|
701
|
-
onOpenChange,
|
|
702
|
-
transport,
|
|
703
|
-
defaultPrompt,
|
|
704
|
-
onError,
|
|
705
|
-
className
|
|
706
|
-
}) {
|
|
707
|
-
const [threadId, setThreadId] = React5.useState(null);
|
|
708
|
-
const [messages, setMessages] = React5.useState([]);
|
|
709
|
-
const [composer, setComposer] = React5.useState(defaultPrompt ?? "");
|
|
710
|
-
const [streamContent, setStreamContent] = React5.useState("");
|
|
711
|
-
const [sending, setSending] = React5.useState(false);
|
|
712
|
-
const [loading, setLoading] = React5.useState(false);
|
|
713
|
-
const abortRef = React5.useRef(null);
|
|
714
|
-
const scrollRef = React5.useRef(null);
|
|
715
|
-
const scopeTokenRef = React5.useRef(0);
|
|
716
|
-
const reportError = React5.useCallback(
|
|
717
|
-
(message) => {
|
|
718
|
-
if (onError) onError(message);
|
|
719
|
-
else if (typeof console !== "undefined") console.error("[ArtifactAgentDock]", message);
|
|
720
|
-
},
|
|
721
|
-
[onError]
|
|
722
|
-
);
|
|
723
|
-
React5.useEffect(() => {
|
|
724
|
-
if (!open) return;
|
|
725
|
-
scopeTokenRef.current += 1;
|
|
726
|
-
abortRef.current?.abort();
|
|
727
|
-
abortRef.current = null;
|
|
728
|
-
let cancelled = false;
|
|
729
|
-
async function bootstrap() {
|
|
730
|
-
setLoading(true);
|
|
731
|
-
try {
|
|
732
|
-
const { id } = await transport.ensureThread(scope);
|
|
733
|
-
if (cancelled) return;
|
|
734
|
-
setThreadId(id);
|
|
735
|
-
if (transport.loadMessages) {
|
|
736
|
-
const prior = await transport.loadMessages(id);
|
|
737
|
-
if (cancelled) return;
|
|
738
|
-
setMessages(
|
|
739
|
-
prior.map((m) => ({
|
|
740
|
-
...m,
|
|
741
|
-
role: normalizeRole(m.role),
|
|
742
|
-
createdAt: m.createdAt instanceof Date ? m.createdAt : m.createdAt ? new Date(m.createdAt) : void 0
|
|
743
|
-
}))
|
|
744
|
-
);
|
|
745
|
-
} else {
|
|
746
|
-
setMessages([]);
|
|
747
|
-
}
|
|
748
|
-
} catch (err) {
|
|
749
|
-
reportError(err instanceof Error ? err.message : "Failed to open artifact chat");
|
|
750
|
-
} finally {
|
|
751
|
-
if (!cancelled) setLoading(false);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
void bootstrap();
|
|
755
|
-
return () => {
|
|
756
|
-
cancelled = true;
|
|
757
|
-
};
|
|
758
|
-
}, [open, scope.kind, scope.key, transport, reportError]);
|
|
759
|
-
React5.useEffect(() => {
|
|
760
|
-
if (!scrollRef.current) return;
|
|
761
|
-
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
762
|
-
}, [messages.length, streamContent]);
|
|
763
|
-
const send = React5.useCallback(async () => {
|
|
764
|
-
const text = composer.trim();
|
|
765
|
-
if (!text || !threadId || sending) return;
|
|
766
|
-
const token = scopeTokenRef.current;
|
|
767
|
-
const userMsg = {
|
|
768
|
-
id: createDockMessageId(),
|
|
769
|
-
role: "user",
|
|
770
|
-
content: text,
|
|
771
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
772
|
-
};
|
|
773
|
-
setMessages((prev) => [...prev, userMsg]);
|
|
774
|
-
setComposer("");
|
|
775
|
-
setSending(true);
|
|
776
|
-
setStreamContent("");
|
|
777
|
-
const controller = new AbortController();
|
|
778
|
-
abortRef.current = controller;
|
|
779
|
-
let assistantContent = "";
|
|
780
|
-
let streamFailed = false;
|
|
781
|
-
try {
|
|
782
|
-
for await (const event of transport.sendStream({ threadId, content: text, signal: controller.signal })) {
|
|
783
|
-
if (scopeTokenRef.current !== token) return;
|
|
784
|
-
if (event.type === "delta") {
|
|
785
|
-
assistantContent += event.text;
|
|
786
|
-
setStreamContent(assistantContent);
|
|
787
|
-
} else if (event.type === "error") {
|
|
788
|
-
streamFailed = true;
|
|
789
|
-
reportError(event.message);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
if (scopeTokenRef.current !== token) return;
|
|
793
|
-
if (assistantContent && !streamFailed) {
|
|
794
|
-
setMessages((prev) => [
|
|
795
|
-
...prev,
|
|
796
|
-
{
|
|
797
|
-
id: createDockMessageId(),
|
|
798
|
-
role: "assistant",
|
|
799
|
-
content: assistantContent,
|
|
800
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
801
|
-
}
|
|
802
|
-
]);
|
|
803
|
-
}
|
|
804
|
-
setStreamContent("");
|
|
805
|
-
} catch (err) {
|
|
806
|
-
if (err?.name === "AbortError") return;
|
|
807
|
-
if (scopeTokenRef.current === token) reportError(err instanceof Error ? err.message : "Send failed");
|
|
808
|
-
} finally {
|
|
809
|
-
if (scopeTokenRef.current === token) setSending(false);
|
|
810
|
-
if (abortRef.current === controller) abortRef.current = null;
|
|
811
|
-
}
|
|
812
|
-
}, [composer, threadId, sending, transport, reportError]);
|
|
813
|
-
const cancel = React5.useCallback(() => {
|
|
814
|
-
if (threadId && transport.cancel) {
|
|
815
|
-
try {
|
|
816
|
-
void Promise.resolve(transport.cancel({ threadId })).catch(() => {
|
|
817
|
-
});
|
|
818
|
-
} catch {
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
abortRef.current?.abort();
|
|
822
|
-
setSending(false);
|
|
823
|
-
setStreamContent("");
|
|
824
|
-
}, [threadId, transport]);
|
|
825
|
-
if (!open) return null;
|
|
826
|
-
const heading = defaultScopeLabel(scope);
|
|
827
|
-
return /* @__PURE__ */ jsxs4(
|
|
828
|
-
"aside",
|
|
829
|
-
{
|
|
830
|
-
className: cn(
|
|
831
|
-
"flex h-full w-[420px] min-w-[360px] max-w-[480px] flex-col border-l border-[var(--md3-outline-variant)] bg-surface-container shadow-[var(--shadow-dropdown)]",
|
|
832
|
-
className
|
|
833
|
-
),
|
|
834
|
-
"aria-label": `Agent chat about ${heading}`,
|
|
835
|
-
children: [
|
|
836
|
-
/* @__PURE__ */ jsxs4("header", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-low px-4 py-3", children: [
|
|
837
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
838
|
-
/* @__PURE__ */ jsx5(Sparkles2, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
839
|
-
/* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
|
|
840
|
-
/* @__PURE__ */ jsx5("div", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Discussing" }),
|
|
841
|
-
/* @__PURE__ */ jsx5("div", { className: "truncate text-sm font-semibold text-foreground", children: heading })
|
|
842
|
-
] })
|
|
843
|
-
] }),
|
|
844
|
-
/* @__PURE__ */ jsx5(
|
|
845
|
-
"button",
|
|
846
|
-
{
|
|
847
|
-
type: "button",
|
|
848
|
-
onClick: () => onOpenChange(false),
|
|
849
|
-
"aria-label": "Close artifact chat",
|
|
850
|
-
className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground",
|
|
851
|
-
children: /* @__PURE__ */ jsx5(X, { className: "h-4 w-4" })
|
|
852
|
-
}
|
|
853
|
-
)
|
|
854
|
-
] }),
|
|
855
|
-
/* @__PURE__ */ jsx5("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-4", children: loading ? /* @__PURE__ */ jsx5("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "Loading conversation\u2026" }) : messages.length === 0 && !streamContent ? /* @__PURE__ */ jsxs4("div", { className: "px-3 py-12 text-center", children: [
|
|
856
|
-
/* @__PURE__ */ jsx5(Sparkles2, { className: "mx-auto mb-3 h-5 w-5 text-muted-foreground/40" }),
|
|
857
|
-
/* @__PURE__ */ jsxs4("p", { className: "text-sm text-foreground", children: [
|
|
858
|
-
"Ask the agent about this ",
|
|
859
|
-
scope.kind === "vault-file" ? "document" : scope.kind,
|
|
860
|
-
"."
|
|
861
|
-
] }),
|
|
862
|
-
/* @__PURE__ */ jsx5("p", { className: "mt-1 text-xs text-muted-foreground", children: "It can read the current contents and propose edits, rewrites, or follow-ups." })
|
|
863
|
-
] }) : /* @__PURE__ */ jsxs4("div", { className: "space-y-3", children: [
|
|
864
|
-
messages.map((m) => /* @__PURE__ */ jsx5(ChatMessage, { role: m.role, content: m.content, timestamp: m.createdAt }, m.id)),
|
|
865
|
-
streamContent && /* @__PURE__ */ jsx5(ChatMessage, { role: "assistant", content: streamContent, isStreaming: true })
|
|
866
|
-
] }) }),
|
|
867
|
-
/* @__PURE__ */ jsx5(
|
|
868
|
-
"form",
|
|
869
|
-
{
|
|
870
|
-
className: "border-t border-[var(--md3-outline-variant)] bg-surface-container-low p-3",
|
|
871
|
-
onSubmit: (e) => {
|
|
872
|
-
e.preventDefault();
|
|
873
|
-
void send();
|
|
874
|
-
},
|
|
875
|
-
children: /* @__PURE__ */ jsxs4("div", { className: "flex items-end gap-2", children: [
|
|
876
|
-
/* @__PURE__ */ jsx5(
|
|
877
|
-
"textarea",
|
|
878
|
-
{
|
|
879
|
-
value: composer,
|
|
880
|
-
onChange: (e) => setComposer(e.target.value),
|
|
881
|
-
onKeyDown: (e) => {
|
|
882
|
-
if (e.key === "Enter" && !e.shiftKey && !sending) {
|
|
883
|
-
e.preventDefault();
|
|
884
|
-
void send();
|
|
885
|
-
}
|
|
886
|
-
},
|
|
887
|
-
placeholder: `Ask about ${heading}\u2026`,
|
|
888
|
-
rows: 2,
|
|
889
|
-
disabled: !threadId,
|
|
890
|
-
className: `min-h-[44px] flex-1 resize-none rounded-[20px] border bg-surface-dim px-3.5 py-2 text-sm text-foreground placeholder:text-muted-foreground disabled:opacity-50 ${focusField2}`
|
|
891
|
-
}
|
|
892
|
-
),
|
|
893
|
-
sending ? /* @__PURE__ */ jsx5(
|
|
894
|
-
"button",
|
|
895
|
-
{
|
|
896
|
-
type: "button",
|
|
897
|
-
onClick: cancel,
|
|
898
|
-
"aria-label": "Cancel",
|
|
899
|
-
className: "inline-flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground",
|
|
900
|
-
children: /* @__PURE__ */ jsx5(Square, { className: "h-4 w-4" })
|
|
901
|
-
}
|
|
902
|
-
) : /* @__PURE__ */ jsx5(
|
|
903
|
-
"button",
|
|
904
|
-
{
|
|
905
|
-
type: "submit",
|
|
906
|
-
disabled: !composer.trim() || !threadId,
|
|
907
|
-
"aria-label": "Send",
|
|
908
|
-
className: "inline-flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50",
|
|
909
|
-
children: /* @__PURE__ */ jsx5(Send, { className: "h-4 w-4" })
|
|
910
|
-
}
|
|
911
|
-
)
|
|
912
|
-
] })
|
|
913
|
-
}
|
|
914
|
-
)
|
|
915
|
-
]
|
|
916
|
-
}
|
|
917
|
-
);
|
|
918
|
-
}
|
|
919
|
-
function createFetchTransport(opts) {
|
|
920
|
-
const threadsUrl = opts.threadsUrl ?? "/api/threads";
|
|
921
|
-
const chatUrl = opts.chatUrl ?? "/api/chat";
|
|
922
|
-
const f = opts.fetchImpl ?? ((...args) => fetch(...args));
|
|
923
|
-
const workspaceId = opts.workspaceId;
|
|
924
|
-
return {
|
|
925
|
-
async ensureThread(scope) {
|
|
926
|
-
const url = threadsUrl.includes("?") ? `${threadsUrl}&workspaceId=${encodeURIComponent(workspaceId)}` : `${threadsUrl}?workspaceId=${encodeURIComponent(workspaceId)}`;
|
|
927
|
-
const res = await f(url, {
|
|
928
|
-
method: "POST",
|
|
929
|
-
headers: { "Content-Type": "application/json" },
|
|
930
|
-
body: JSON.stringify({ workspaceId, scope, ensureScoped: true })
|
|
931
|
-
});
|
|
932
|
-
if (!res.ok) throw new Error(`Thread ensure failed (${res.status})`);
|
|
933
|
-
const data = await res.json();
|
|
934
|
-
const id = data.thread?.id ?? data.id;
|
|
935
|
-
if (!id) throw new Error("Malformed ensure response");
|
|
936
|
-
return { id };
|
|
937
|
-
},
|
|
938
|
-
async loadMessages(threadId) {
|
|
939
|
-
const res = await f(`${threadsUrl}/${encodeURIComponent(threadId)}/messages`);
|
|
940
|
-
if (!res.ok) return [];
|
|
941
|
-
const data = await res.json();
|
|
942
|
-
return (data.messages ?? []).map((m) => ({
|
|
943
|
-
id: m.id,
|
|
944
|
-
role: normalizeRole(m.role),
|
|
945
|
-
content: m.content,
|
|
946
|
-
createdAt: new Date(m.createdAt)
|
|
947
|
-
}));
|
|
948
|
-
},
|
|
949
|
-
sendStream({ threadId, content, signal }) {
|
|
950
|
-
return (async function* () {
|
|
951
|
-
const res = await f(chatUrl, {
|
|
952
|
-
method: "POST",
|
|
953
|
-
headers: { "Content-Type": "application/json" },
|
|
954
|
-
body: JSON.stringify({ workspaceId, threadId, content }),
|
|
955
|
-
signal
|
|
956
|
-
});
|
|
957
|
-
if (!res.ok || !res.body) {
|
|
958
|
-
const text = await res.text().catch(() => "");
|
|
959
|
-
yield { type: "error", message: text || `Send failed (${res.status})` };
|
|
960
|
-
return;
|
|
961
|
-
}
|
|
962
|
-
const reader = res.body.getReader();
|
|
963
|
-
const decoder = new TextDecoder();
|
|
964
|
-
let buffer = "";
|
|
965
|
-
while (true) {
|
|
966
|
-
const { done, value } = await reader.read();
|
|
967
|
-
if (done) break;
|
|
968
|
-
buffer += decoder.decode(value, { stream: true });
|
|
969
|
-
const lines = buffer.split("\n");
|
|
970
|
-
buffer = lines.pop() ?? "";
|
|
971
|
-
for (const line of lines) {
|
|
972
|
-
if (!line.trim()) continue;
|
|
973
|
-
try {
|
|
974
|
-
const event = JSON.parse(line);
|
|
975
|
-
if (event.type === "message.part.updated" && event.data) {
|
|
976
|
-
const delta = event.data.delta;
|
|
977
|
-
const part = event.data.part;
|
|
978
|
-
const isText = !part || typeof part !== "object" || part.type === void 0 || part.type === "text";
|
|
979
|
-
if (isText && typeof delta === "string" && delta.length > 0) {
|
|
980
|
-
yield { type: "delta", text: delta };
|
|
981
|
-
}
|
|
982
|
-
} else if (event.type === "error" && event.data) {
|
|
983
|
-
const message = typeof event.data.message === "string" ? event.data.message : "Stream error";
|
|
984
|
-
yield { type: "error", message };
|
|
985
|
-
}
|
|
986
|
-
} catch {
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
})();
|
|
991
|
-
}
|
|
992
|
-
};
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
export {
|
|
996
|
-
DEFAULT_REASONING_LEVEL_OPTIONS,
|
|
997
|
-
HARNESS_REASONING_OPTIONS,
|
|
998
|
-
ReasoningLevelPicker,
|
|
999
|
-
AgentProfilePicker,
|
|
1000
|
-
modelProvider,
|
|
1001
|
-
snapHarnessToModel,
|
|
1002
|
-
ArtifactAgentDock,
|
|
1003
|
-
createFetchTransport,
|
|
1004
|
-
AgentTimeline,
|
|
1005
|
-
ChatContainer,
|
|
1006
|
-
ChatMessage2 as ChatMessage,
|
|
1007
|
-
MessageList,
|
|
1008
|
-
ThinkingIndicator,
|
|
1009
|
-
UserMessage
|
|
1010
|
-
};
|