@tangle-network/sandbox-ui 0.97.0 → 0.99.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/dist/{WorkflowGraph-OPLQFAJK.js → WorkflowGraph-DB5F6ZSF.js} +1 -1
- package/dist/chat.d.ts +83 -273
- package/dist/chat.js +1 -10
- package/dist/{chunk-A4AABEX4.js → chunk-G7CFCZQ7.js} +951 -1593
- package/dist/{chunk-22JEBRUV.js → chunk-JIZCCGD2.js} +1 -1
- package/dist/{chunk-5XRAKVIV.js → chunk-RQZRXKMR.js} +1 -5
- package/dist/{chunk-VQZ6XDRR.js → chunk-Y4OTBMZS.js} +730 -444
- package/dist/dashboard.d.ts +8 -86
- package/dist/dashboard.js +8 -24
- package/dist/globals.css +17 -98
- package/dist/index.d.ts +4 -4
- package/dist/index.js +10 -32
- package/dist/{model-brand-CuXKfTMh.d.ts → model-brand-BNnaUKKZ.d.ts} +2 -4
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1 -2
- package/dist/styles.css +17 -98
- package/dist/workbench.js +3 -5
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js +2 -2
- package/dist/workspace.d.ts +1 -1
- package/package.json +2 -1
- package/dist/chunk-WMVUJ5SA.js +0 -529
- package/dist/chunk-WSN2RZC6.js +0 -292
|
@@ -1,29 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatBytes
|
|
3
3
|
} from "./chunk-TK46XFLM.js";
|
|
4
|
-
import {
|
|
5
|
-
HARNESS_OPTIONS,
|
|
6
|
-
HarnessLogo,
|
|
7
|
-
chatCapableHarnesses
|
|
8
|
-
} from "./chunk-WSN2RZC6.js";
|
|
9
|
-
import {
|
|
10
|
-
ModelPicker,
|
|
11
|
-
PickerMenuFooter,
|
|
12
|
-
PickerMenuMeta,
|
|
13
|
-
PickerMenuSearch,
|
|
14
|
-
PickerMenuSection,
|
|
15
|
-
PickerMenuTag,
|
|
16
|
-
isTextChatModel,
|
|
17
|
-
pickerMenuBodyClass,
|
|
18
|
-
pickerMenuContentClass,
|
|
19
|
-
pickerMenuItemClass,
|
|
20
|
-
stripBrandPrefix
|
|
21
|
-
} from "./chunk-WMVUJ5SA.js";
|
|
22
|
-
import {
|
|
23
|
-
ModelBrandStack,
|
|
24
|
-
canonicalModelId,
|
|
25
|
-
resolveModelBrandIdentity
|
|
26
|
-
} from "./chunk-5XRAKVIV.js";
|
|
27
4
|
import {
|
|
28
5
|
cn
|
|
29
6
|
} from "./chunk-EI44GEQ5.js";
|
|
@@ -46,22 +23,6 @@ import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
|
|
46
23
|
import { Brain, ChevronDown, Sparkles } from "lucide-react";
|
|
47
24
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
48
25
|
var REASONING_LADDER = reasoningLadder;
|
|
49
|
-
function clampReasoningLevel(value, available) {
|
|
50
|
-
if (value === "auto" || !available || available.includes(value)) {
|
|
51
|
-
return value;
|
|
52
|
-
}
|
|
53
|
-
const requestedRank = REASONING_LADDER.indexOf(value);
|
|
54
|
-
let closest;
|
|
55
|
-
let closestRank = -1;
|
|
56
|
-
for (const effort of available) {
|
|
57
|
-
const rank = REASONING_LADDER.indexOf(effort);
|
|
58
|
-
if (rank <= requestedRank && rank > closestRank) {
|
|
59
|
-
closest = effort;
|
|
60
|
-
closestRank = rank;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return closest ?? "auto";
|
|
64
|
-
}
|
|
65
26
|
function ReasoningGlyph({ level }) {
|
|
66
27
|
if (level === "auto") {
|
|
67
28
|
return /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 text-muted-foreground" });
|
|
@@ -184,1573 +145,973 @@ function ReasoningLevelPicker({
|
|
|
184
145
|
] });
|
|
185
146
|
}
|
|
186
147
|
|
|
187
|
-
// src/chat/agent-
|
|
188
|
-
import {
|
|
189
|
-
harnessHonorsEffort,
|
|
190
|
-
harnessHonorsModel,
|
|
191
|
-
reasoningEffortsFor
|
|
192
|
-
} from "@tangle-network/agent-interface";
|
|
193
|
-
import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
|
|
194
|
-
import { ChevronDown as ChevronDown3, SlidersHorizontal } from "lucide-react";
|
|
195
|
-
import * as React5 from "react";
|
|
196
|
-
|
|
197
|
-
// src/chat/harness-model-compat.ts
|
|
148
|
+
// src/chat/agent-composer.tsx
|
|
198
149
|
import {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// src/chat/informative-lock.tsx
|
|
211
|
-
import { Lock, Plus } from "lucide-react";
|
|
212
|
-
import * as React3 from "react";
|
|
213
|
-
|
|
214
|
-
// src/lib/use-click-outside.ts
|
|
150
|
+
ArrowUp,
|
|
151
|
+
Folder,
|
|
152
|
+
Loader2,
|
|
153
|
+
Paperclip,
|
|
154
|
+
RotateCcw,
|
|
155
|
+
Square,
|
|
156
|
+
Upload,
|
|
157
|
+
X
|
|
158
|
+
} from "lucide-react";
|
|
215
159
|
import * as React from "react";
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
160
|
+
|
|
161
|
+
// src/chat/attachment-validation.ts
|
|
162
|
+
function isAcceptedType(file, accept) {
|
|
163
|
+
if (!accept || accept.trim().length === 0) return true;
|
|
164
|
+
const patterns = accept.split(",").map((pattern) => pattern.trim()).filter((pattern) => pattern.length > 0);
|
|
165
|
+
if (patterns.length === 0) return true;
|
|
166
|
+
const name = file.name.toLowerCase();
|
|
167
|
+
const type = (file.type || "").toLowerCase();
|
|
168
|
+
return patterns.some((pattern) => {
|
|
169
|
+
const lowerPattern = pattern.toLowerCase();
|
|
170
|
+
if (lowerPattern.startsWith(".")) {
|
|
171
|
+
return name.endsWith(lowerPattern);
|
|
226
172
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
173
|
+
if (lowerPattern.endsWith("/*")) {
|
|
174
|
+
const prefix = lowerPattern.slice(0, -1);
|
|
175
|
+
return type.startsWith(prefix);
|
|
176
|
+
}
|
|
177
|
+
return type === lowerPattern;
|
|
178
|
+
});
|
|
231
179
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
React2.useLayoutEffect(() => {
|
|
261
|
-
place();
|
|
262
|
-
window.addEventListener("resize", place);
|
|
263
|
-
window.addEventListener("scroll", place, true);
|
|
264
|
-
return () => {
|
|
265
|
-
window.removeEventListener("resize", place);
|
|
266
|
-
window.removeEventListener("scroll", place, true);
|
|
267
|
-
};
|
|
268
|
-
}, [place]);
|
|
269
|
-
return createPortal(
|
|
270
|
-
/* @__PURE__ */ jsx2(
|
|
271
|
-
"div",
|
|
272
|
-
{
|
|
273
|
-
...rest,
|
|
274
|
-
ref: setRefs,
|
|
275
|
-
className: cn("fixed z-50", className),
|
|
276
|
-
style: {
|
|
277
|
-
...style,
|
|
278
|
-
pointerEvents: "auto",
|
|
279
|
-
...position ?? { visibility: "hidden", left: 0, top: 0 }
|
|
280
|
-
},
|
|
281
|
-
children
|
|
282
|
-
}
|
|
283
|
-
),
|
|
284
|
-
document.body
|
|
285
|
-
);
|
|
180
|
+
function validateComposerFiles(files, config) {
|
|
181
|
+
const { maxSizeBytes, maxCount, currentCount = 0, accept } = config;
|
|
182
|
+
const list = Array.isArray(files) ? files : Array.from(files);
|
|
183
|
+
const accepted = [];
|
|
184
|
+
const rejected = [];
|
|
185
|
+
for (const file of list) {
|
|
186
|
+
if (!isAcceptedType(file, accept)) {
|
|
187
|
+
rejected.push({
|
|
188
|
+
file,
|
|
189
|
+
reason: `"${file.name}" is not an accepted file type (${accept}).`
|
|
190
|
+
});
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (maxSizeBytes !== void 0 && file.size > maxSizeBytes) {
|
|
194
|
+
rejected.push({
|
|
195
|
+
file,
|
|
196
|
+
reason: `"${file.name}" (${formatBytes(file.size)}) exceeds the ${formatBytes(maxSizeBytes)} limit.`
|
|
197
|
+
});
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (maxCount !== void 0 && currentCount + accepted.length >= maxCount) {
|
|
201
|
+
rejected.push({
|
|
202
|
+
file,
|
|
203
|
+
reason: `"${file.name}" was not added \u2014 the ${maxCount}-file limit is already reached.`
|
|
204
|
+
});
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
accepted.push(file);
|
|
286
208
|
}
|
|
287
|
-
|
|
209
|
+
return { accepted, rejected };
|
|
210
|
+
}
|
|
288
211
|
|
|
289
|
-
// src/chat/
|
|
290
|
-
import { jsx as
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
212
|
+
// src/chat/agent-composer.tsx
|
|
213
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
214
|
+
var IMAGE_EXTENSION_BY_MIME = {
|
|
215
|
+
"image/png": "png",
|
|
216
|
+
"image/jpeg": "jpg",
|
|
217
|
+
"image/jpg": "jpg",
|
|
218
|
+
"image/gif": "gif",
|
|
219
|
+
"image/webp": "webp",
|
|
220
|
+
"image/bmp": "bmp",
|
|
221
|
+
"image/svg+xml": "svg"
|
|
222
|
+
};
|
|
223
|
+
function isGenericImageName(name) {
|
|
224
|
+
return /^(image)?(\.[a-z0-9]+)?$/i.test(name.trim());
|
|
225
|
+
}
|
|
226
|
+
function imageExtension(file) {
|
|
227
|
+
const fromMime = IMAGE_EXTENSION_BY_MIME[file.type.toLowerCase()];
|
|
228
|
+
if (fromMime) return fromMime;
|
|
229
|
+
const fromName = /\.([a-z0-9]+)$/i.exec(file.name)?.[1];
|
|
230
|
+
return fromName?.toLowerCase() ?? "png";
|
|
231
|
+
}
|
|
232
|
+
var MentionEditor = React.lazy(() => import("./mention-editor-3Q6MYOIU.js"));
|
|
233
|
+
function AgentComposer({
|
|
234
|
+
value,
|
|
235
|
+
onChange,
|
|
236
|
+
onSubmit,
|
|
237
|
+
placeholder = "Send a message\u2026",
|
|
238
|
+
disabled,
|
|
239
|
+
busy,
|
|
240
|
+
onCancel,
|
|
241
|
+
controls,
|
|
242
|
+
trailing,
|
|
243
|
+
onAttach,
|
|
244
|
+
onAttachFolder,
|
|
245
|
+
attachments = [],
|
|
246
|
+
onRemoveFile,
|
|
247
|
+
onRetryFile,
|
|
248
|
+
accept,
|
|
249
|
+
onRejectFiles,
|
|
250
|
+
dropTitle = "Drop files to add context",
|
|
251
|
+
dropDescription = "They attach to your next message.",
|
|
252
|
+
canSubmitWhileBusy = false,
|
|
253
|
+
canSubmitAttachmentsOnly = false,
|
|
254
|
+
mention,
|
|
255
|
+
focusShortcut = false,
|
|
256
|
+
minRows = 2,
|
|
257
|
+
maxHeight = 200,
|
|
298
258
|
className,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
side = "top"
|
|
259
|
+
sendLabel = "Send",
|
|
260
|
+
autoFocus
|
|
302
261
|
}) {
|
|
303
|
-
const
|
|
304
|
-
const
|
|
305
|
-
const
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
262
|
+
const textareaRef = React.useRef(null);
|
|
263
|
+
const richFocusRef = React.useRef(null);
|
|
264
|
+
const fileInputRef = React.useRef(null);
|
|
265
|
+
const folderInputRef = React.useRef(null);
|
|
266
|
+
const [dragOver, setDragOver] = React.useState(false);
|
|
267
|
+
const dragDepth = React.useRef(0);
|
|
268
|
+
const pastedImageCount = React.useRef(0);
|
|
269
|
+
const resize = React.useCallback(
|
|
270
|
+
(el) => {
|
|
271
|
+
el.style.height = "auto";
|
|
272
|
+
el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`;
|
|
273
|
+
},
|
|
274
|
+
[maxHeight]
|
|
275
|
+
);
|
|
276
|
+
React.useEffect(() => {
|
|
277
|
+
if (textareaRef.current) resize(textareaRef.current);
|
|
278
|
+
}, [value, resize]);
|
|
279
|
+
React.useEffect(() => {
|
|
280
|
+
if (!focusShortcut || disabled) return;
|
|
281
|
+
function onKeyDown(event) {
|
|
282
|
+
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "l") {
|
|
283
|
+
event.preventDefault();
|
|
284
|
+
if (mention) richFocusRef.current?.();
|
|
285
|
+
else textareaRef.current?.focus();
|
|
286
|
+
}
|
|
311
287
|
}
|
|
288
|
+
document.addEventListener("keydown", onKeyDown);
|
|
289
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
290
|
+
}, [focusShortcut, disabled, mention]);
|
|
291
|
+
const canSend = !disabled && (!busy || canSubmitWhileBusy) && (value.trim().length > 0 || canSubmitAttachmentsOnly && attachments.length > 0);
|
|
292
|
+
const submit = () => {
|
|
293
|
+
if (!canSend) return;
|
|
294
|
+
onSubmit();
|
|
295
|
+
};
|
|
296
|
+
const registerRichFocus = React.useCallback((focus) => {
|
|
297
|
+
richFocusRef.current = focus;
|
|
312
298
|
}, []);
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
"
|
|
320
|
-
{
|
|
321
|
-
ref,
|
|
322
|
-
className: cn("relative inline-flex", className),
|
|
323
|
-
onMouseEnter: () => {
|
|
324
|
-
cancelClose();
|
|
325
|
-
setOpen(true);
|
|
326
|
-
},
|
|
327
|
-
onMouseLeave: scheduleClose,
|
|
328
|
-
onKeyDown: (event) => {
|
|
329
|
-
if (event.key === "Escape") setOpen(false);
|
|
330
|
-
},
|
|
331
|
-
children: [
|
|
332
|
-
/* @__PURE__ */ jsx3(
|
|
333
|
-
"button",
|
|
334
|
-
{
|
|
335
|
-
type: "button",
|
|
336
|
-
onClick: () => setOpen(true),
|
|
337
|
-
"data-state": open ? "open" : "closed",
|
|
338
|
-
"aria-label": ariaLabel,
|
|
339
|
-
"aria-haspopup": "dialog",
|
|
340
|
-
"aria-expanded": open,
|
|
341
|
-
className: cn(
|
|
342
|
-
"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",
|
|
343
|
-
"text-xs font-medium text-foreground shadow-sm transition-colors",
|
|
344
|
-
"hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
345
|
-
"data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
|
|
346
|
-
triggerClassName
|
|
347
|
-
),
|
|
348
|
-
children
|
|
349
|
-
}
|
|
350
|
-
),
|
|
351
|
-
open && /* @__PURE__ */ jsxs2(
|
|
352
|
-
AnchoredPopover,
|
|
353
|
-
{
|
|
354
|
-
ref: popoverRef,
|
|
355
|
-
anchorRef: ref,
|
|
356
|
-
side,
|
|
357
|
-
role: "dialog",
|
|
358
|
-
onMouseEnter: () => {
|
|
359
|
-
cancelClose();
|
|
360
|
-
setOpen(true);
|
|
361
|
-
},
|
|
362
|
-
onMouseLeave: scheduleClose,
|
|
363
|
-
className: cn(
|
|
364
|
-
"w-72 rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-3",
|
|
365
|
-
"shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
|
|
366
|
-
popoverClassName
|
|
367
|
-
),
|
|
368
|
-
children: [
|
|
369
|
-
/* @__PURE__ */ jsxs2("p", { className: "flex items-start gap-2 text-sm font-medium text-foreground", children: [
|
|
370
|
-
/* @__PURE__ */ jsx3(Lock, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
371
|
-
/* @__PURE__ */ jsx3("span", { children: lockTitle })
|
|
372
|
-
] }),
|
|
373
|
-
/* @__PURE__ */ jsx3("p", { className: "mt-1.5 text-xs text-muted-foreground", children: lockBody }),
|
|
374
|
-
/* @__PURE__ */ jsxs2(
|
|
375
|
-
"button",
|
|
376
|
-
{
|
|
377
|
-
type: "button",
|
|
378
|
-
onClick: () => {
|
|
379
|
-
setOpen(false);
|
|
380
|
-
onNewChat();
|
|
381
|
-
},
|
|
382
|
-
className: cn(
|
|
383
|
-
"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",
|
|
384
|
-
"bg-primary/10 ring-1 ring-inset ring-primary/25 transition-colors",
|
|
385
|
-
"hover:bg-primary/15 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
386
|
-
),
|
|
387
|
-
children: [
|
|
388
|
-
/* @__PURE__ */ jsx3(Plus, { className: "h-4 w-4" }),
|
|
389
|
-
newChatLabel
|
|
390
|
-
]
|
|
391
|
-
}
|
|
392
|
-
)
|
|
393
|
-
]
|
|
394
|
-
}
|
|
395
|
-
)
|
|
396
|
-
]
|
|
397
|
-
}
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// src/chat/agent-profile-picker.tsx
|
|
402
|
-
import { Bot, Check, ChevronDown as ChevronDown2, Pencil, Plus as Plus2, Trash2 } from "lucide-react";
|
|
403
|
-
import * as React4 from "react";
|
|
404
|
-
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
405
|
-
function profileSubtitle(profile) {
|
|
406
|
-
if (profile.description) return profile.description;
|
|
407
|
-
const count = profile.capabilities?.length ?? 0;
|
|
408
|
-
return `${count} ${count === 1 ? "tool group" : "tool groups"}`;
|
|
409
|
-
}
|
|
410
|
-
function AgentProfilePicker({
|
|
411
|
-
value,
|
|
412
|
-
onChange,
|
|
413
|
-
profiles,
|
|
414
|
-
capabilities,
|
|
415
|
-
onCreate,
|
|
416
|
-
onUpdate,
|
|
417
|
-
onDelete,
|
|
418
|
-
disabled,
|
|
419
|
-
locked,
|
|
420
|
-
lockReason,
|
|
421
|
-
onNewChat,
|
|
422
|
-
className,
|
|
423
|
-
triggerClassName,
|
|
424
|
-
popoverClassName,
|
|
425
|
-
side = "top"
|
|
426
|
-
}) {
|
|
427
|
-
const [open, setOpen] = React4.useState(false);
|
|
428
|
-
const [composing, setComposing] = React4.useState(
|
|
429
|
-
null
|
|
430
|
-
);
|
|
431
|
-
const popoverRef = React4.useRef(null);
|
|
432
|
-
const ref = useClickOutside(() => {
|
|
433
|
-
setOpen(false);
|
|
434
|
-
setComposing(null);
|
|
435
|
-
}, [popoverRef]);
|
|
436
|
-
const canAuthor = Boolean(capabilities && (onCreate || onUpdate));
|
|
437
|
-
const builtins = profiles.filter((profile) => profile.builtin);
|
|
438
|
-
const custom = profiles.filter((profile) => !profile.builtin);
|
|
439
|
-
const selected = profiles.find((profile) => profile.id === value);
|
|
440
|
-
const selectedName = selected?.name ?? value;
|
|
441
|
-
React4.useEffect(() => {
|
|
442
|
-
if (!locked) return;
|
|
443
|
-
setOpen(false);
|
|
444
|
-
setComposing(null);
|
|
445
|
-
}, [locked]);
|
|
446
|
-
function select(id) {
|
|
447
|
-
onChange(id);
|
|
448
|
-
setOpen(false);
|
|
449
|
-
}
|
|
450
|
-
async function save(draft) {
|
|
451
|
-
if (draft.id) await onUpdate?.(draft.id, draft);
|
|
452
|
-
else await onCreate?.(draft);
|
|
453
|
-
setComposing(null);
|
|
454
|
-
}
|
|
455
|
-
if (locked && onNewChat) {
|
|
456
|
-
return /* @__PURE__ */ jsxs3(
|
|
457
|
-
InformativeLock,
|
|
458
|
-
{
|
|
459
|
-
ariaLabel: "Agent profile (locked)",
|
|
460
|
-
lockTitle: `Profile fixed to ${selectedName} for this conversation.`,
|
|
461
|
-
lockBody: "Conversations stay on one profile.",
|
|
462
|
-
newChatLabel: "New chat to switch profile",
|
|
463
|
-
onNewChat,
|
|
464
|
-
className,
|
|
465
|
-
triggerClassName,
|
|
466
|
-
popoverClassName,
|
|
467
|
-
side,
|
|
468
|
-
children: [
|
|
469
|
-
/* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
470
|
-
/* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName })
|
|
471
|
-
]
|
|
472
|
-
}
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
return /* @__PURE__ */ jsxs3("div", { ref, className: cn("relative inline-flex", className), children: [
|
|
476
|
-
/* @__PURE__ */ jsxs3(
|
|
477
|
-
"button",
|
|
478
|
-
{
|
|
479
|
-
type: "button",
|
|
480
|
-
disabled: disabled || locked,
|
|
481
|
-
title: locked ? lockReason : void 0,
|
|
482
|
-
onClick: () => setOpen((v) => !v),
|
|
483
|
-
className: cn(
|
|
484
|
-
"inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5",
|
|
485
|
-
"text-xs font-medium text-foreground shadow-sm transition-colors",
|
|
486
|
-
"hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
487
|
-
"data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
|
|
488
|
-
"disabled:cursor-not-allowed disabled:opacity-60",
|
|
489
|
-
triggerClassName
|
|
490
|
-
),
|
|
491
|
-
"data-state": open ? "open" : "closed",
|
|
492
|
-
"aria-label": "Agent profile",
|
|
493
|
-
children: [
|
|
494
|
-
/* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
495
|
-
/* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName }),
|
|
496
|
-
!locked && /* @__PURE__ */ jsx4(ChevronDown2, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
|
|
497
|
-
]
|
|
498
|
-
}
|
|
499
|
-
),
|
|
500
|
-
open && !locked && /* @__PURE__ */ jsx4(
|
|
501
|
-
AnchoredPopover,
|
|
502
|
-
{
|
|
503
|
-
ref: popoverRef,
|
|
504
|
-
anchorRef: ref,
|
|
505
|
-
side,
|
|
506
|
-
className: cn(
|
|
507
|
-
"w-[min(320px,calc(100vw-2rem))] overflow-hidden rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-1",
|
|
508
|
-
"shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
|
|
509
|
-
popoverClassName
|
|
510
|
-
),
|
|
511
|
-
"data-testid": "agent-profile-popover",
|
|
512
|
-
children: composing ? /* @__PURE__ */ jsx4(
|
|
513
|
-
ProfileForm,
|
|
514
|
-
{
|
|
515
|
-
capabilities: capabilities ?? [],
|
|
516
|
-
initial: composing === "new" ? void 0 : composing,
|
|
517
|
-
onCancel: () => setComposing(null),
|
|
518
|
-
onSave: save
|
|
519
|
-
}
|
|
520
|
-
) : /* @__PURE__ */ jsxs3("div", { className: "max-h-[min(60vh,420px)] overflow-y-auto", children: [
|
|
521
|
-
/* @__PURE__ */ jsx4(SectionHeader, { children: "Modes" }),
|
|
522
|
-
builtins.map((profile) => /* @__PURE__ */ jsx4(
|
|
523
|
-
ProfileRow,
|
|
524
|
-
{
|
|
525
|
-
active: profile.id === value,
|
|
526
|
-
title: profile.name,
|
|
527
|
-
subtitle: profileSubtitle(profile),
|
|
528
|
-
onSelect: () => select(profile.id)
|
|
529
|
-
},
|
|
530
|
-
profile.id
|
|
531
|
-
)),
|
|
532
|
-
custom.length > 0 && /* @__PURE__ */ jsx4(SectionHeader, { children: "Your agents" }),
|
|
533
|
-
custom.map((profile) => /* @__PURE__ */ jsx4(
|
|
534
|
-
ProfileRow,
|
|
535
|
-
{
|
|
536
|
-
active: profile.id === value,
|
|
537
|
-
title: profile.name,
|
|
538
|
-
subtitle: profileSubtitle(profile),
|
|
539
|
-
onSelect: () => select(profile.id),
|
|
540
|
-
onEdit: canAuthor ? () => setComposing(profile) : void 0,
|
|
541
|
-
onDelete: onDelete ? () => void onDelete(profile.id) : void 0
|
|
542
|
-
},
|
|
543
|
-
profile.id
|
|
544
|
-
)),
|
|
545
|
-
canAuthor && /* @__PURE__ */ jsxs3(
|
|
546
|
-
"button",
|
|
547
|
-
{
|
|
548
|
-
type: "button",
|
|
549
|
-
onClick: () => setComposing("new"),
|
|
550
|
-
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",
|
|
551
|
-
children: [
|
|
552
|
-
/* @__PURE__ */ jsx4(Plus2, { className: "h-4 w-4" }),
|
|
553
|
-
" New agent\u2026"
|
|
554
|
-
]
|
|
555
|
-
}
|
|
556
|
-
)
|
|
557
|
-
] })
|
|
558
|
-
}
|
|
559
|
-
)
|
|
560
|
-
] });
|
|
561
|
-
}
|
|
562
|
-
function SectionHeader({ children }) {
|
|
563
|
-
return /* @__PURE__ */ jsx4("div", { className: "px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children });
|
|
564
|
-
}
|
|
565
|
-
function ProfileRow({
|
|
566
|
-
active,
|
|
567
|
-
title,
|
|
568
|
-
subtitle,
|
|
569
|
-
onSelect,
|
|
570
|
-
onEdit,
|
|
571
|
-
onDelete
|
|
572
|
-
}) {
|
|
573
|
-
return /* @__PURE__ */ jsxs3(
|
|
574
|
-
"div",
|
|
575
|
-
{
|
|
576
|
-
className: cn(
|
|
577
|
-
"group flex items-center gap-2 rounded-md px-2.5 py-2 text-sm transition-colors",
|
|
578
|
-
active ? "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]" : "hover:bg-accent/40"
|
|
579
|
-
),
|
|
580
|
-
children: [
|
|
581
|
-
/* @__PURE__ */ jsxs3(
|
|
582
|
-
"button",
|
|
583
|
-
{
|
|
584
|
-
type: "button",
|
|
585
|
-
onClick: onSelect,
|
|
586
|
-
className: "flex min-w-0 flex-1 items-center gap-2 text-left",
|
|
587
|
-
children: [
|
|
588
|
-
/* @__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" }) }),
|
|
589
|
-
/* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
|
|
590
|
-
/* @__PURE__ */ jsx4("span", { className: "block truncate font-medium", children: title }),
|
|
591
|
-
/* @__PURE__ */ jsx4("span", { className: "block truncate text-xs text-muted-foreground", children: subtitle })
|
|
592
|
-
] })
|
|
593
|
-
]
|
|
594
|
-
}
|
|
595
|
-
),
|
|
596
|
-
(onEdit || onDelete) && /* @__PURE__ */ jsxs3("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100", children: [
|
|
597
|
-
onEdit && /* @__PURE__ */ jsx4(
|
|
598
|
-
"button",
|
|
599
|
-
{
|
|
600
|
-
type: "button",
|
|
601
|
-
onClick: onEdit,
|
|
602
|
-
title: "Edit",
|
|
603
|
-
className: "rounded p-1 text-muted-foreground hover:text-foreground",
|
|
604
|
-
children: /* @__PURE__ */ jsx4(Pencil, { className: "h-3 w-3" })
|
|
605
|
-
}
|
|
606
|
-
),
|
|
607
|
-
onDelete && /* @__PURE__ */ jsx4(
|
|
608
|
-
"button",
|
|
609
|
-
{
|
|
610
|
-
type: "button",
|
|
611
|
-
onClick: onDelete,
|
|
612
|
-
title: "Delete",
|
|
613
|
-
className: "rounded p-1 text-muted-foreground hover:text-[var(--status-error,#ff4d6d)]",
|
|
614
|
-
children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3 w-3" })
|
|
615
|
-
}
|
|
616
|
-
)
|
|
617
|
-
] })
|
|
618
|
-
]
|
|
619
|
-
}
|
|
620
|
-
);
|
|
621
|
-
}
|
|
622
|
-
function ProfileForm({
|
|
623
|
-
capabilities,
|
|
624
|
-
initial,
|
|
625
|
-
onCancel,
|
|
626
|
-
onSave
|
|
627
|
-
}) {
|
|
628
|
-
const [name, setName] = React4.useState(initial?.name ?? "");
|
|
629
|
-
const [caps, setCaps] = React4.useState(
|
|
630
|
-
initial?.capabilities ? [...initial.capabilities] : capabilities.map((c) => c.id)
|
|
631
|
-
);
|
|
632
|
-
const [instructions, setInstructions] = React4.useState(initial?.instructions ?? "");
|
|
633
|
-
const toggle = (id) => setCaps(
|
|
634
|
-
(prev) => prev.includes(id) ? prev.filter((c) => c !== id) : [...prev, id]
|
|
635
|
-
);
|
|
636
|
-
return /* @__PURE__ */ jsxs3("div", { className: "space-y-3 p-2.5", children: [
|
|
637
|
-
/* @__PURE__ */ jsx4(
|
|
638
|
-
"input",
|
|
639
|
-
{
|
|
640
|
-
autoFocus: true,
|
|
641
|
-
value: name,
|
|
642
|
-
onChange: (event) => setName(event.target.value),
|
|
643
|
-
placeholder: "Agent name (e.g. Tax Reviewer)",
|
|
644
|
-
className: "w-full rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
|
|
645
|
-
}
|
|
646
|
-
),
|
|
647
|
-
capabilities.length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
|
|
648
|
-
/* @__PURE__ */ jsx4("p", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children: "Tools" }),
|
|
649
|
-
/* @__PURE__ */ jsx4("div", { className: "space-y-0.5", children: capabilities.map((cap) => /* @__PURE__ */ jsxs3(
|
|
650
|
-
"label",
|
|
651
|
-
{
|
|
652
|
-
className: "flex cursor-pointer items-start gap-2 rounded-md px-2 py-1.5 hover:bg-accent/40",
|
|
653
|
-
children: [
|
|
654
|
-
/* @__PURE__ */ jsx4(
|
|
655
|
-
"input",
|
|
656
|
-
{
|
|
657
|
-
type: "checkbox",
|
|
658
|
-
checked: caps.includes(cap.id),
|
|
659
|
-
onChange: () => toggle(cap.id),
|
|
660
|
-
className: "mt-0.5"
|
|
661
|
-
}
|
|
662
|
-
),
|
|
663
|
-
/* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
|
|
664
|
-
/* @__PURE__ */ jsx4("span", { className: "block text-sm font-medium", children: cap.label }),
|
|
665
|
-
cap.description && /* @__PURE__ */ jsx4("span", { className: "block text-xs text-muted-foreground", children: cap.description })
|
|
666
|
-
] })
|
|
667
|
-
]
|
|
668
|
-
},
|
|
669
|
-
cap.id
|
|
670
|
-
)) })
|
|
671
|
-
] }),
|
|
672
|
-
/* @__PURE__ */ jsx4(
|
|
673
|
-
"textarea",
|
|
674
|
-
{
|
|
675
|
-
value: instructions,
|
|
676
|
-
onChange: (event) => setInstructions(event.target.value),
|
|
677
|
-
rows: 2,
|
|
678
|
-
placeholder: "Instructions (optional) \u2014 how this agent should behave",
|
|
679
|
-
className: "w-full resize-none rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
|
|
680
|
-
}
|
|
681
|
-
),
|
|
682
|
-
/* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-2", children: [
|
|
683
|
-
/* @__PURE__ */ jsx4(
|
|
684
|
-
"button",
|
|
685
|
-
{
|
|
686
|
-
type: "button",
|
|
687
|
-
onClick: onCancel,
|
|
688
|
-
className: "rounded-lg px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent/40",
|
|
689
|
-
children: "Cancel"
|
|
690
|
-
}
|
|
691
|
-
),
|
|
692
|
-
/* @__PURE__ */ jsx4(
|
|
693
|
-
"button",
|
|
694
|
-
{
|
|
695
|
-
type: "button",
|
|
696
|
-
disabled: !name.trim(),
|
|
697
|
-
onClick: () => onSave({
|
|
698
|
-
id: initial?.id,
|
|
699
|
-
name: name.trim(),
|
|
700
|
-
capabilities: caps,
|
|
701
|
-
instructions: instructions.trim()
|
|
702
|
-
}),
|
|
703
|
-
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",
|
|
704
|
-
children: "Save agent"
|
|
705
|
-
}
|
|
706
|
-
)
|
|
707
|
-
] })
|
|
708
|
-
] });
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
// src/chat/agent-session-controls.tsx
|
|
712
|
-
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
713
|
-
function harnessAutonomyNote(harness) {
|
|
714
|
-
const ownModel = !harnessHonorsModel(harness);
|
|
715
|
-
const ownEffort = !harnessHonorsEffort(harness);
|
|
716
|
-
if (ownModel && ownEffort) return "own model + thinking";
|
|
717
|
-
if (ownModel) return "own model";
|
|
718
|
-
if (ownEffort) return "own thinking";
|
|
719
|
-
return null;
|
|
720
|
-
}
|
|
721
|
-
var HARNESS_GROUP_LABELS = {
|
|
722
|
-
steerable: "Uses your model & thinking",
|
|
723
|
-
fixed: "Brings its own setup",
|
|
724
|
-
"no-agent": "No agent"
|
|
725
|
-
};
|
|
726
|
-
var HARNESS_GROUP_ORDER = [
|
|
727
|
-
"steerable",
|
|
728
|
-
"fixed",
|
|
729
|
-
"no-agent"
|
|
730
|
-
];
|
|
731
|
-
function harnessGroup(option) {
|
|
732
|
-
if (!option.chatCapable) return "no-agent";
|
|
733
|
-
return harnessAutonomyNote(option.type) === null ? "steerable" : "fixed";
|
|
734
|
-
}
|
|
735
|
-
var HARNESS_SEARCH_THRESHOLD = 7;
|
|
736
|
-
function HarnessDropdown({
|
|
737
|
-
value,
|
|
738
|
-
onChange,
|
|
739
|
-
available,
|
|
740
|
-
disabled,
|
|
741
|
-
locked,
|
|
742
|
-
lockReason,
|
|
743
|
-
onNewChat,
|
|
744
|
-
lockTitle,
|
|
745
|
-
lockBody,
|
|
746
|
-
newChatLabel,
|
|
747
|
-
side = "bottom",
|
|
748
|
-
align = "start",
|
|
749
|
-
avoidCollisions,
|
|
750
|
-
triggerClassName
|
|
751
|
-
}) {
|
|
752
|
-
const allowed = new Set(
|
|
753
|
-
available ?? HARNESS_OPTIONS.map((h) => h.type)
|
|
754
|
-
);
|
|
755
|
-
const options = HARNESS_OPTIONS.filter((h) => allowed.has(h.type));
|
|
756
|
-
const selected = options.find((option) => option.type === value);
|
|
757
|
-
const [query, setQuery] = React5.useState("");
|
|
758
|
-
const showSearch = options.length > HARNESS_SEARCH_THRESHOLD;
|
|
759
|
-
const filtered = React5.useMemo(() => {
|
|
760
|
-
const q = query.trim().toLowerCase();
|
|
761
|
-
if (!q) return options;
|
|
762
|
-
return options.filter(
|
|
763
|
-
(o) => o.label.toLowerCase().includes(q) || o.type.toLowerCase().includes(q) || (o.description?.toLowerCase() ?? "").includes(q)
|
|
764
|
-
);
|
|
765
|
-
}, [options, query]);
|
|
766
|
-
const sections = React5.useMemo(() => {
|
|
767
|
-
const byGroup = /* @__PURE__ */ new Map();
|
|
768
|
-
for (const option of filtered) {
|
|
769
|
-
const key = harnessGroup(option);
|
|
770
|
-
const bucket = byGroup.get(key);
|
|
771
|
-
if (bucket) bucket.push(option);
|
|
772
|
-
else byGroup.set(key, [option]);
|
|
773
|
-
}
|
|
774
|
-
return HARNESS_GROUP_ORDER.map((key) => ({
|
|
775
|
-
key,
|
|
776
|
-
label: HARNESS_GROUP_LABELS[key],
|
|
777
|
-
items: byGroup.get(key) ?? []
|
|
778
|
-
})).filter((section) => section.items.length > 0);
|
|
779
|
-
}, [filtered]);
|
|
780
|
-
if (locked && onNewChat) {
|
|
781
|
-
const label = selected?.label ?? value;
|
|
782
|
-
return /* @__PURE__ */ jsxs4(
|
|
783
|
-
InformativeLock,
|
|
784
|
-
{
|
|
785
|
-
ariaLabel: "Agent harness (locked)",
|
|
786
|
-
lockTitle: lockTitle ?? `Agent fixed to ${label} for this conversation.`,
|
|
787
|
-
lockBody: lockBody ?? "Conversations stay on one agent.",
|
|
788
|
-
newChatLabel: newChatLabel ?? "New chat to switch agent",
|
|
789
|
-
onNewChat,
|
|
790
|
-
triggerClassName,
|
|
791
|
-
children: [
|
|
792
|
-
/* @__PURE__ */ jsx5(HarnessLogo, { type: value, size: 16 }),
|
|
793
|
-
/* @__PURE__ */ jsx5("span", { children: label })
|
|
794
|
-
]
|
|
795
|
-
}
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
return /* @__PURE__ */ jsxs4(
|
|
799
|
-
DropdownMenu2.Root,
|
|
800
|
-
{
|
|
801
|
-
onOpenChange: (nextOpen) => {
|
|
802
|
-
if (!nextOpen) setQuery("");
|
|
803
|
-
},
|
|
804
|
-
children: [
|
|
805
|
-
/* @__PURE__ */ jsx5(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs4(
|
|
806
|
-
"button",
|
|
807
|
-
{
|
|
808
|
-
type: "button",
|
|
809
|
-
disabled: disabled || locked,
|
|
810
|
-
title: locked ? lockReason : void 0,
|
|
811
|
-
className: cn(
|
|
812
|
-
"inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5",
|
|
813
|
-
"text-xs font-medium text-foreground shadow-sm transition-colors",
|
|
814
|
-
"hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
815
|
-
"data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
|
|
816
|
-
"disabled:cursor-not-allowed disabled:opacity-60",
|
|
817
|
-
triggerClassName
|
|
818
|
-
),
|
|
819
|
-
"aria-label": "Agent harness",
|
|
820
|
-
children: [
|
|
821
|
-
/* @__PURE__ */ jsx5(HarnessLogo, { type: value, size: 16, className: "shrink-0" }),
|
|
822
|
-
/* @__PURE__ */ jsx5("span", { className: "truncate", children: selected?.label ?? value }),
|
|
823
|
-
!locked && /* @__PURE__ */ jsx5(ChevronDown3, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
|
|
824
|
-
]
|
|
825
|
-
}
|
|
826
|
-
) }),
|
|
827
|
-
/* @__PURE__ */ jsx5(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs4(
|
|
828
|
-
DropdownMenu2.Content,
|
|
829
|
-
{
|
|
830
|
-
side,
|
|
831
|
-
align,
|
|
832
|
-
avoidCollisions,
|
|
833
|
-
collisionPadding: 24,
|
|
834
|
-
sideOffset: 6,
|
|
835
|
-
className: pickerMenuContentClass,
|
|
836
|
-
children: [
|
|
837
|
-
showSearch && /* @__PURE__ */ jsx5(
|
|
838
|
-
PickerMenuSearch,
|
|
839
|
-
{
|
|
840
|
-
value: query,
|
|
841
|
-
onChange: setQuery,
|
|
842
|
-
placeholder: "Search agents..."
|
|
843
|
-
}
|
|
844
|
-
),
|
|
845
|
-
/* @__PURE__ */ jsx5("div", { className: pickerMenuBodyClass, children: sections.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "No agents match." }) : sections.map((section) => /* @__PURE__ */ jsx5(PickerMenuSection, { label: section.label, children: section.items.map((option) => /* @__PURE__ */ jsxs4(
|
|
846
|
-
DropdownMenu2.Item,
|
|
847
|
-
{
|
|
848
|
-
onSelect: (event) => {
|
|
849
|
-
event.preventDefault();
|
|
850
|
-
onChange(option.type);
|
|
851
|
-
},
|
|
852
|
-
className: pickerMenuItemClass({
|
|
853
|
-
active: option.type === value
|
|
854
|
-
}),
|
|
855
|
-
children: [
|
|
856
|
-
/* @__PURE__ */ jsx5(
|
|
857
|
-
HarnessLogo,
|
|
858
|
-
{
|
|
859
|
-
type: option.type,
|
|
860
|
-
size: 20,
|
|
861
|
-
className: "mt-0.5"
|
|
862
|
-
}
|
|
863
|
-
),
|
|
864
|
-
/* @__PURE__ */ jsxs4("div", { className: "min-w-0 flex-1", children: [
|
|
865
|
-
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1.5", children: [
|
|
866
|
-
/* @__PURE__ */ jsx5("span", { className: "min-w-0 truncate text-sm font-medium", children: option.label }),
|
|
867
|
-
harnessAutonomyNote(option.type) && /* @__PURE__ */ jsx5(PickerMenuTag, { children: harnessAutonomyNote(option.type) })
|
|
868
|
-
] }),
|
|
869
|
-
/* @__PURE__ */ jsx5(PickerMenuMeta, { parts: [option.description] })
|
|
870
|
-
] })
|
|
871
|
-
]
|
|
872
|
-
},
|
|
873
|
-
option.type
|
|
874
|
-
)) }, section.key)) }),
|
|
875
|
-
showSearch && /* @__PURE__ */ jsxs4(PickerMenuFooter, { children: [
|
|
876
|
-
filtered.length,
|
|
877
|
-
" of ",
|
|
878
|
-
options.length,
|
|
879
|
-
" agent",
|
|
880
|
-
options.length === 1 ? "" : "s"
|
|
881
|
-
] })
|
|
882
|
-
]
|
|
883
|
-
}
|
|
884
|
-
) })
|
|
885
|
-
]
|
|
886
|
-
}
|
|
887
|
-
);
|
|
888
|
-
}
|
|
889
|
-
function modelReasoningCapability(models, canonicalId) {
|
|
890
|
-
if (!models || !canonicalId) return null;
|
|
891
|
-
const entry = models.find((m) => canonicalModelId(m) === canonicalId);
|
|
892
|
-
return entry ? {
|
|
893
|
-
supportsReasoning: entry.supportsReasoning,
|
|
894
|
-
maxEffort: entry.maxReasoningEffort
|
|
895
|
-
} : null;
|
|
896
|
-
}
|
|
897
|
-
function AgentSessionControls({
|
|
898
|
-
harness,
|
|
899
|
-
profile,
|
|
900
|
-
model,
|
|
901
|
-
reasoning,
|
|
902
|
-
filterModelsToHarness,
|
|
903
|
-
trailing,
|
|
904
|
-
className,
|
|
905
|
-
context = "chat",
|
|
906
|
-
layout = "inline",
|
|
907
|
-
menuPlacement = "auto"
|
|
908
|
-
}) {
|
|
909
|
-
if (!harness && !profile && !model && !reasoning && !trailing) return null;
|
|
910
|
-
const restrictModelsToHarness = Boolean(
|
|
911
|
-
harness && (harness.locked || filterModelsToHarness)
|
|
912
|
-
);
|
|
913
|
-
const selectorHonorsModel = !harness || harnessHonorsModel(harness.value);
|
|
914
|
-
const selectorHonorsEffort = !harness || harnessHonorsEffort(harness.value);
|
|
915
|
-
const clampEffort = (harnessValue, modelId) => {
|
|
916
|
-
if (!reasoning) return;
|
|
917
|
-
const available = reasoningEffortsFor(
|
|
918
|
-
harnessValue,
|
|
919
|
-
modelReasoningCapability(model?.models, modelId)
|
|
920
|
-
);
|
|
921
|
-
const clamped = clampReasoningLevel(reasoning.value, available);
|
|
922
|
-
if (clamped !== reasoning.value) reasoning.onChange(clamped);
|
|
299
|
+
const dropEnabled = Boolean(onAttach);
|
|
300
|
+
const handleDragEnter = (event) => {
|
|
301
|
+
if (!dropEnabled) return;
|
|
302
|
+
event.preventDefault();
|
|
303
|
+
event.stopPropagation();
|
|
304
|
+
dragDepth.current++;
|
|
305
|
+
if (event.dataTransfer?.types.includes("Files")) setDragOver(true);
|
|
923
306
|
};
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
}
|
|
307
|
+
const handleDragLeave = (event) => {
|
|
308
|
+
if (!dropEnabled) return;
|
|
309
|
+
event.preventDefault();
|
|
310
|
+
event.stopPropagation();
|
|
311
|
+
dragDepth.current--;
|
|
312
|
+
if (dragDepth.current <= 0) {
|
|
313
|
+
dragDepth.current = 0;
|
|
314
|
+
setDragOver(false);
|
|
933
315
|
}
|
|
934
|
-
clampEffort(next, nextModelId);
|
|
935
316
|
};
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
if (snapped !== harness.value) {
|
|
942
|
-
harness.onChange(snapped);
|
|
943
|
-
nextHarness = snapped;
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
if (nextHarness) clampEffort(nextHarness, nextModelId);
|
|
317
|
+
const handleDragOver = (event) => {
|
|
318
|
+
if (!dropEnabled) return;
|
|
319
|
+
event.preventDefault();
|
|
320
|
+
event.stopPropagation();
|
|
321
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
|
|
947
322
|
};
|
|
948
|
-
const
|
|
949
|
-
|
|
950
|
-
(
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
)
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
side: menuSide,
|
|
975
|
-
align: menuAlign,
|
|
976
|
-
avoidCollisions,
|
|
977
|
-
triggerClassName: secondaryTriggerClass
|
|
978
|
-
}
|
|
979
|
-
);
|
|
980
|
-
const profileNode = profile && /* @__PURE__ */ jsx5(
|
|
981
|
-
AgentProfilePicker,
|
|
982
|
-
{
|
|
983
|
-
value: profile.value,
|
|
984
|
-
onChange: profile.onChange,
|
|
985
|
-
profiles: profile.profiles,
|
|
986
|
-
capabilities: profile.capabilities,
|
|
987
|
-
onCreate: profile.onCreate,
|
|
988
|
-
onUpdate: profile.onUpdate,
|
|
989
|
-
onDelete: profile.onDelete,
|
|
990
|
-
disabled: profile.disabled,
|
|
991
|
-
locked: profile.locked,
|
|
992
|
-
lockReason: profile.lockReason,
|
|
993
|
-
onNewChat: profile.onNewChat,
|
|
994
|
-
side: inlineForceDown || collapsed ? "bottom" : "top",
|
|
995
|
-
triggerClassName: secondaryTriggerClass,
|
|
996
|
-
popoverClassName: collapsed ? "w-full" : void 0
|
|
997
|
-
}
|
|
998
|
-
);
|
|
999
|
-
const modelNode = model && /* @__PURE__ */ jsx5(
|
|
1000
|
-
ModelPicker,
|
|
1001
|
-
{
|
|
1002
|
-
variant: "pill",
|
|
1003
|
-
label: "",
|
|
1004
|
-
value: model.value,
|
|
1005
|
-
onChange: handleModelChange,
|
|
1006
|
-
models: visibleModels ?? [],
|
|
1007
|
-
loading: model.loading,
|
|
1008
|
-
popular: model.popular,
|
|
1009
|
-
recents: model.recents,
|
|
1010
|
-
modalities: model.modalities,
|
|
1011
|
-
excludeProviders: model.excludeProviders,
|
|
1012
|
-
disabled: model.disabled || (visibleModels ?? []).length === 0 || !selectorHonorsModel,
|
|
1013
|
-
side: collapsed ? void 0 : "bottom",
|
|
1014
|
-
avoidCollisions,
|
|
1015
|
-
triggerClassName: modelTriggerClass
|
|
1016
|
-
}
|
|
1017
|
-
);
|
|
1018
|
-
const modelReasoning = modelReasoningCapability(model?.models, model?.value);
|
|
1019
|
-
const reasoningOptions = (harness && HARNESS_REASONING_OPTIONS[harness.value]) ?? reasoning?.options;
|
|
1020
|
-
const reasoningNode = reasoning && /* @__PURE__ */ jsx5(
|
|
1021
|
-
ReasoningLevelPicker,
|
|
1022
|
-
{
|
|
1023
|
-
value: reasoning.value,
|
|
1024
|
-
onChange: reasoning.onChange,
|
|
1025
|
-
options: reasoningOptions,
|
|
1026
|
-
disabled: reasoning.disabled || !selectorHonorsEffort,
|
|
1027
|
-
available: harness ? reasoningEffortsFor(harness.value, modelReasoning) : reasoning.available,
|
|
1028
|
-
side: collapsed ? void 0 : "bottom",
|
|
1029
|
-
avoidCollisions,
|
|
1030
|
-
triggerClassName: secondaryTriggerClass
|
|
1031
|
-
}
|
|
1032
|
-
);
|
|
1033
|
-
if (layout === "gear") {
|
|
1034
|
-
return /* @__PURE__ */ jsx5(
|
|
1035
|
-
GearControls,
|
|
1036
|
-
{
|
|
1037
|
-
className,
|
|
1038
|
-
profileNode,
|
|
1039
|
-
harnessNode,
|
|
1040
|
-
modelNode,
|
|
1041
|
-
reasoningNode,
|
|
1042
|
-
trailing
|
|
1043
|
-
}
|
|
1044
|
-
);
|
|
1045
|
-
}
|
|
1046
|
-
if (layout === "combined") {
|
|
1047
|
-
const segments = [];
|
|
1048
|
-
if (harness) {
|
|
1049
|
-
const harnessLabel = HARNESS_OPTIONS.find((option) => option.type === harness.value)?.label ?? harness.value;
|
|
1050
|
-
segments.push({
|
|
1051
|
-
key: "harness",
|
|
1052
|
-
label: harnessLabel,
|
|
1053
|
-
node: /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1.5", children: [
|
|
1054
|
-
/* @__PURE__ */ jsx5(HarnessLogo, { type: harness.value, size: 16, className: "shrink-0" }),
|
|
1055
|
-
harnessLabel
|
|
1056
|
-
] })
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1059
|
-
if (model && selectorHonorsModel) {
|
|
1060
|
-
const current = (model.models ?? []).find(
|
|
1061
|
-
(entry) => canonicalModelId(entry) === model.value
|
|
1062
|
-
);
|
|
1063
|
-
const identity = current ? resolveModelBrandIdentity(current) : null;
|
|
1064
|
-
const modelLabel = stripBrandPrefix(
|
|
1065
|
-
current?.name ?? current?.id ?? model.value,
|
|
1066
|
-
identity
|
|
1067
|
-
);
|
|
1068
|
-
segments.push({
|
|
1069
|
-
key: "model",
|
|
1070
|
-
label: modelLabel,
|
|
1071
|
-
node: /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1.5", children: [
|
|
1072
|
-
identity && /* @__PURE__ */ jsx5(ModelBrandStack, { identity, size: "sm" }),
|
|
1073
|
-
modelLabel
|
|
1074
|
-
] })
|
|
1075
|
-
});
|
|
1076
|
-
}
|
|
1077
|
-
if (reasoning && selectorHonorsEffort) {
|
|
1078
|
-
const effortLabel = (reasoningOptions ?? DEFAULT_REASONING_LEVEL_OPTIONS).find(
|
|
1079
|
-
(option) => option.value === reasoning.value
|
|
1080
|
-
)?.label ?? "Auto";
|
|
1081
|
-
segments.push({
|
|
1082
|
-
key: "effort",
|
|
1083
|
-
label: effortLabel,
|
|
1084
|
-
node: /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1.5", children: [
|
|
1085
|
-
/* @__PURE__ */ jsx5("span", { className: "flex w-3.5 shrink-0 justify-center", children: /* @__PURE__ */ jsx5(ReasoningGlyph, { level: reasoning.value }) }),
|
|
1086
|
-
effortLabel
|
|
1087
|
-
] })
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
const summary = /* @__PURE__ */ jsx5("span", { className: "flex items-center gap-1.5", children: segments.map((segment, index) => /* @__PURE__ */ jsxs4(React5.Fragment, { children: [
|
|
1091
|
-
index > 0 && /* @__PURE__ */ jsx5(
|
|
1092
|
-
"span",
|
|
1093
|
-
{
|
|
1094
|
-
className: "text-base leading-none text-muted-foreground/60",
|
|
1095
|
-
"aria-hidden": true,
|
|
1096
|
-
children: "\xB7"
|
|
1097
|
-
}
|
|
1098
|
-
),
|
|
1099
|
-
segment.node
|
|
1100
|
-
] }, segment.key)) });
|
|
1101
|
-
return /* @__PURE__ */ jsx5(
|
|
1102
|
-
SummaryControls,
|
|
1103
|
-
{
|
|
1104
|
-
className,
|
|
1105
|
-
summary,
|
|
1106
|
-
title: segments.map((segment) => segment.label).join(" \xB7 "),
|
|
1107
|
-
contentSide: menuPlacement === "down" ? "bottom" : "top",
|
|
1108
|
-
profileNode,
|
|
1109
|
-
harnessNode,
|
|
1110
|
-
modelNode,
|
|
1111
|
-
reasoningNode,
|
|
1112
|
-
trailing
|
|
1113
|
-
}
|
|
1114
|
-
);
|
|
1115
|
-
}
|
|
1116
|
-
return /* @__PURE__ */ jsxs4(
|
|
1117
|
-
"div",
|
|
1118
|
-
{
|
|
1119
|
-
className: cn("flex flex-wrap items-center gap-2", className),
|
|
1120
|
-
"data-testid": "agent-session-controls",
|
|
1121
|
-
children: [
|
|
1122
|
-
profileNode,
|
|
1123
|
-
harnessNode,
|
|
1124
|
-
modelNode,
|
|
1125
|
-
reasoningNode,
|
|
1126
|
-
trailing && /* @__PURE__ */ jsx5("div", { className: "ml-auto flex items-center gap-2", children: trailing })
|
|
1127
|
-
]
|
|
1128
|
-
}
|
|
1129
|
-
);
|
|
1130
|
-
}
|
|
1131
|
-
function GearControls({
|
|
1132
|
-
className,
|
|
1133
|
-
profileNode,
|
|
1134
|
-
harnessNode,
|
|
1135
|
-
modelNode,
|
|
1136
|
-
reasoningNode,
|
|
1137
|
-
trailing
|
|
1138
|
-
}) {
|
|
1139
|
-
return /* @__PURE__ */ jsx5(
|
|
1140
|
-
CollapsibleControls,
|
|
1141
|
-
{
|
|
1142
|
-
className,
|
|
1143
|
-
trigger: /* @__PURE__ */ jsx5(SlidersHorizontal, { className: "h-4 w-4 text-muted-foreground" }),
|
|
1144
|
-
triggerClassName: cn(
|
|
1145
|
-
"inline-flex h-8 w-8 items-center justify-center rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container",
|
|
1146
|
-
"text-foreground shadow-sm transition-colors",
|
|
1147
|
-
"hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
1148
|
-
"data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high"
|
|
1149
|
-
),
|
|
1150
|
-
contentSide: "top",
|
|
1151
|
-
contentClassName: cn(
|
|
1152
|
-
"w-56 gap-2 border-[var(--md3-outline-variant)] bg-surface-container-highest p-2",
|
|
1153
|
-
"shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]"
|
|
1154
|
-
),
|
|
1155
|
-
profileNode,
|
|
1156
|
-
harnessNode,
|
|
1157
|
-
modelNode,
|
|
1158
|
-
reasoningNode,
|
|
1159
|
-
trailing
|
|
1160
|
-
}
|
|
1161
|
-
);
|
|
1162
|
-
}
|
|
1163
|
-
function GearSection({
|
|
1164
|
-
label,
|
|
1165
|
-
children
|
|
1166
|
-
}) {
|
|
1167
|
-
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
|
|
1168
|
-
/* @__PURE__ */ jsx5("span", { className: "px-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70", children: label }),
|
|
1169
|
-
children
|
|
1170
|
-
] });
|
|
1171
|
-
}
|
|
1172
|
-
function SummaryControls({
|
|
1173
|
-
className,
|
|
1174
|
-
summary,
|
|
1175
|
-
title,
|
|
1176
|
-
contentSide,
|
|
1177
|
-
profileNode,
|
|
1178
|
-
harnessNode,
|
|
1179
|
-
modelNode,
|
|
1180
|
-
reasoningNode,
|
|
1181
|
-
trailing
|
|
1182
|
-
}) {
|
|
1183
|
-
return /* @__PURE__ */ jsx5(
|
|
1184
|
-
CollapsibleControls,
|
|
1185
|
-
{
|
|
1186
|
-
className,
|
|
1187
|
-
trigger: /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1188
|
-
summary,
|
|
1189
|
-
/* @__PURE__ */ jsx5(ChevronDown3, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })
|
|
1190
|
-
] }),
|
|
1191
|
-
triggerTitle: title,
|
|
1192
|
-
triggerClassName: cn(
|
|
1193
|
-
// Container-less trigger (Codex-style): bare label + chevron docked in
|
|
1194
|
-
// the composer footer, no border/card/shadow. Only a subtle rounded
|
|
1195
|
-
// hover/open tint marks it as interactive.
|
|
1196
|
-
"inline-flex h-8 items-center gap-1.5 rounded-md px-1.5",
|
|
1197
|
-
"whitespace-nowrap text-xs font-medium text-foreground transition-colors",
|
|
1198
|
-
"hover:bg-muted/50 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
1199
|
-
"data-[state=open]:bg-muted/50"
|
|
1200
|
-
),
|
|
1201
|
-
contentSide,
|
|
1202
|
-
contentClassName: "w-64 gap-2.5 border-border bg-popover p-2.5 text-popover-foreground shadow-lg",
|
|
1203
|
-
profileNode,
|
|
1204
|
-
harnessNode,
|
|
1205
|
-
modelNode,
|
|
1206
|
-
reasoningNode,
|
|
1207
|
-
trailing
|
|
323
|
+
const deliverFiles = (files) => {
|
|
324
|
+
if (!onAttach || files.length === 0) return;
|
|
325
|
+
const { accepted, rejected } = validateComposerFiles(files, { accept });
|
|
326
|
+
if (rejected.length > 0) onRejectFiles?.(rejected);
|
|
327
|
+
if (accepted.length === 0) return;
|
|
328
|
+
const dt = new DataTransfer();
|
|
329
|
+
for (const file of accepted) dt.items.add(file);
|
|
330
|
+
onAttach(dt.files);
|
|
331
|
+
};
|
|
332
|
+
const handleDrop = (event) => {
|
|
333
|
+
if (!dropEnabled) return;
|
|
334
|
+
event.preventDefault();
|
|
335
|
+
event.stopPropagation();
|
|
336
|
+
dragDepth.current = 0;
|
|
337
|
+
setDragOver(false);
|
|
338
|
+
const files = event.dataTransfer?.files;
|
|
339
|
+
if (files?.length) deliverFiles(Array.from(files));
|
|
340
|
+
};
|
|
341
|
+
const renamePastedFiles = (files) => files.map((file) => {
|
|
342
|
+
if (file.type.startsWith("image/") && isGenericImageName(file.name)) {
|
|
343
|
+
pastedImageCount.current += 1;
|
|
344
|
+
return new File(
|
|
345
|
+
[file],
|
|
346
|
+
`pasted-image-${pastedImageCount.current}.${imageExtension(file)}`,
|
|
347
|
+
{ type: file.type }
|
|
348
|
+
);
|
|
1208
349
|
}
|
|
350
|
+
return file;
|
|
351
|
+
});
|
|
352
|
+
const handlePaste = (event) => {
|
|
353
|
+
if (!onAttach) return;
|
|
354
|
+
const clipboardFiles = event.clipboardData?.files;
|
|
355
|
+
if (!clipboardFiles || clipboardFiles.length === 0) return;
|
|
356
|
+
event.preventDefault();
|
|
357
|
+
deliverFiles(renamePastedFiles(Array.from(clipboardFiles)));
|
|
358
|
+
};
|
|
359
|
+
const handleEditorPasteFiles = (clipboardFiles) => {
|
|
360
|
+
if (!onAttach || clipboardFiles.length === 0) return false;
|
|
361
|
+
deliverFiles(renamePastedFiles(Array.from(clipboardFiles)));
|
|
362
|
+
return true;
|
|
363
|
+
};
|
|
364
|
+
const folderChips = attachments.filter((file) => file.kind === "folder");
|
|
365
|
+
const fileChips = attachments.filter((file) => file.kind !== "folder");
|
|
366
|
+
const attachButtonClass = cn(
|
|
367
|
+
"flex size-8 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors",
|
|
368
|
+
"hover:bg-surface-container-highest hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
369
|
+
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
1209
370
|
);
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
371
|
+
return /* @__PURE__ */ jsxs2("div", { className: cn("relative", className), children: [
|
|
372
|
+
dragOver && /* @__PURE__ */ jsx2("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-surface-container-high/95", children: /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
|
|
373
|
+
/* @__PURE__ */ jsx2("span", { className: "mx-auto mb-2 flex size-11 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx2(Upload, { className: "size-5" }) }),
|
|
374
|
+
/* @__PURE__ */ jsx2("p", { className: "font-semibold text-foreground text-sm", children: dropTitle }),
|
|
375
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-0.5 text-muted-foreground text-xs", children: dropDescription })
|
|
376
|
+
] }) }),
|
|
377
|
+
/* @__PURE__ */ jsxs2(
|
|
378
|
+
"div",
|
|
379
|
+
{
|
|
380
|
+
"data-testid": "agent-composer",
|
|
381
|
+
onDragEnter: handleDragEnter,
|
|
382
|
+
onDragLeave: handleDragLeave,
|
|
383
|
+
onDragOver: handleDragOver,
|
|
384
|
+
onDrop: handleDrop,
|
|
385
|
+
className: cn(
|
|
386
|
+
"flex flex-col gap-2 rounded-2xl border border-[var(--md3-outline-variant)] bg-surface-container-high p-2.5 shadow-sm transition-colors",
|
|
387
|
+
"focus-within:border-primary/60",
|
|
388
|
+
disabled && "opacity-60"
|
|
389
|
+
),
|
|
390
|
+
children: [
|
|
391
|
+
attachments.length > 0 && /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((file) => {
|
|
392
|
+
const isError = file.status === "error";
|
|
393
|
+
const isPending = file.status === "pending";
|
|
394
|
+
return /* @__PURE__ */ jsxs2(
|
|
395
|
+
"span",
|
|
396
|
+
{
|
|
397
|
+
title: isError ? file.errorMessage : void 0,
|
|
398
|
+
className: cn(
|
|
399
|
+
"inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs",
|
|
400
|
+
isError ? "border-[var(--status-error,#ff4d6d)]/40 text-[var(--status-error,#ff4d6d)]" : "border-[var(--md3-outline-variant)] bg-surface-container text-foreground",
|
|
401
|
+
isPending && "opacity-60"
|
|
402
|
+
),
|
|
403
|
+
children: [
|
|
404
|
+
file.kind !== "folder" && file.previewUrl ? /* @__PURE__ */ jsx2(
|
|
405
|
+
"img",
|
|
406
|
+
{
|
|
407
|
+
src: file.previewUrl,
|
|
408
|
+
alt: "",
|
|
409
|
+
className: "size-8 rounded object-cover"
|
|
410
|
+
}
|
|
411
|
+
) : file.kind === "folder" ? /* @__PURE__ */ jsx2(Folder, { className: "size-3 shrink-0" }) : /* @__PURE__ */ jsx2(Paperclip, { className: "size-3 shrink-0" }),
|
|
412
|
+
/* @__PURE__ */ jsx2("span", { className: "max-w-[150px] truncate", children: file.name }),
|
|
413
|
+
file.fileCount !== void 0 && /* @__PURE__ */ jsxs2("span", { className: "text-muted-foreground", children: [
|
|
414
|
+
"(",
|
|
415
|
+
file.fileCount,
|
|
416
|
+
")"
|
|
417
|
+
] }),
|
|
418
|
+
file.status === "uploading" && /* @__PURE__ */ jsx2(Loader2, { className: "size-3 animate-spin" }),
|
|
419
|
+
isError && file.errorMessage && /* @__PURE__ */ jsx2("span", { className: "max-w-[150px] truncate text-[var(--status-error,#ff4d6d)]/80", children: file.errorMessage }),
|
|
420
|
+
isError && onRetryFile && /* @__PURE__ */ jsx2(
|
|
421
|
+
"button",
|
|
422
|
+
{
|
|
423
|
+
type: "button",
|
|
424
|
+
"aria-label": `Retry upload ${file.name}`,
|
|
425
|
+
onClick: () => onRetryFile(file.id),
|
|
426
|
+
className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
427
|
+
children: /* @__PURE__ */ jsx2(RotateCcw, { className: "size-3" })
|
|
428
|
+
}
|
|
429
|
+
),
|
|
430
|
+
onRemoveFile && /* @__PURE__ */ jsx2(
|
|
431
|
+
"button",
|
|
432
|
+
{
|
|
433
|
+
type: "button",
|
|
434
|
+
"aria-label": `Remove ${file.name}`,
|
|
435
|
+
onClick: () => onRemoveFile(file.id),
|
|
436
|
+
className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
437
|
+
children: /* @__PURE__ */ jsx2(X, { className: "size-3" })
|
|
438
|
+
}
|
|
439
|
+
)
|
|
440
|
+
]
|
|
441
|
+
},
|
|
442
|
+
file.id
|
|
443
|
+
);
|
|
444
|
+
}) }),
|
|
445
|
+
mention ? /* @__PURE__ */ jsx2(
|
|
446
|
+
React.Suspense,
|
|
1234
447
|
{
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
448
|
+
fallback: /* @__PURE__ */ jsx2(
|
|
449
|
+
"textarea",
|
|
450
|
+
{
|
|
451
|
+
rows: minRows,
|
|
452
|
+
value,
|
|
453
|
+
disabled: true,
|
|
454
|
+
placeholder,
|
|
455
|
+
"aria-label": "Message input",
|
|
456
|
+
className: cn(
|
|
457
|
+
"w-full resize-none bg-transparent px-2 py-1 text-sm leading-relaxed text-foreground outline-none",
|
|
458
|
+
"placeholder:text-muted-foreground"
|
|
459
|
+
)
|
|
460
|
+
}
|
|
461
|
+
),
|
|
462
|
+
children: /* @__PURE__ */ jsx2(
|
|
463
|
+
MentionEditor,
|
|
464
|
+
{
|
|
465
|
+
value,
|
|
466
|
+
onChange,
|
|
467
|
+
onSubmit: submit,
|
|
468
|
+
placeholder,
|
|
469
|
+
disabled,
|
|
470
|
+
autoFocus,
|
|
471
|
+
minRows,
|
|
472
|
+
maxHeight,
|
|
473
|
+
mention,
|
|
474
|
+
registerFocus: registerRichFocus,
|
|
475
|
+
onPasteFiles: onAttach ? handleEditorPasteFiles : void 0
|
|
476
|
+
}
|
|
477
|
+
)
|
|
1240
478
|
}
|
|
1241
|
-
)
|
|
1242
|
-
|
|
1243
|
-
DropdownMenu2.Content,
|
|
479
|
+
) : /* @__PURE__ */ jsx2(
|
|
480
|
+
"textarea",
|
|
1244
481
|
{
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
482
|
+
ref: textareaRef,
|
|
483
|
+
rows: minRows,
|
|
484
|
+
value,
|
|
485
|
+
disabled,
|
|
486
|
+
autoFocus,
|
|
487
|
+
onChange: (event) => onChange(event.target.value),
|
|
488
|
+
onInput: (event) => resize(event.currentTarget),
|
|
489
|
+
onPaste: handlePaste,
|
|
490
|
+
onKeyDown: (event) => {
|
|
491
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing && event.keyCode !== 229) {
|
|
1253
492
|
event.preventDefault();
|
|
493
|
+
submit();
|
|
1254
494
|
}
|
|
1255
495
|
},
|
|
496
|
+
placeholder,
|
|
497
|
+
"aria-label": "Message input",
|
|
1256
498
|
className: cn(
|
|
1257
|
-
"
|
|
1258
|
-
"
|
|
1259
|
-
|
|
1260
|
-
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
1261
|
-
contentClassName
|
|
1262
|
-
),
|
|
1263
|
-
children: [
|
|
1264
|
-
profileNode && /* @__PURE__ */ jsx5(GearSection, { label: "Agent", children: profileNode }),
|
|
1265
|
-
harnessNode && /* @__PURE__ */ jsx5(GearSection, { label: "Harness", children: harnessNode }),
|
|
1266
|
-
modelNode && /* @__PURE__ */ jsx5(GearSection, { label: "Model", children: modelNode }),
|
|
1267
|
-
reasoningNode && /* @__PURE__ */ jsx5(GearSection, { label: "Effort", children: reasoningNode })
|
|
1268
|
-
]
|
|
499
|
+
"w-full resize-none bg-transparent px-2 py-1 text-sm leading-relaxed text-foreground outline-none",
|
|
500
|
+
"placeholder:text-muted-foreground"
|
|
501
|
+
)
|
|
1269
502
|
}
|
|
1270
|
-
)
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
503
|
+
),
|
|
504
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-end gap-2", children: [
|
|
505
|
+
onAttach && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
506
|
+
/* @__PURE__ */ jsx2(
|
|
507
|
+
"button",
|
|
508
|
+
{
|
|
509
|
+
type: "button",
|
|
510
|
+
onClick: () => fileInputRef.current?.click(),
|
|
511
|
+
disabled,
|
|
512
|
+
"aria-label": "Attach files",
|
|
513
|
+
title: "Attach files",
|
|
514
|
+
className: attachButtonClass,
|
|
515
|
+
children: /* @__PURE__ */ jsx2(Paperclip, { className: "size-4" })
|
|
516
|
+
}
|
|
517
|
+
),
|
|
518
|
+
/* @__PURE__ */ jsx2(
|
|
519
|
+
"input",
|
|
520
|
+
{
|
|
521
|
+
ref: fileInputRef,
|
|
522
|
+
type: "file",
|
|
523
|
+
multiple: true,
|
|
524
|
+
className: "hidden",
|
|
525
|
+
accept,
|
|
526
|
+
onChange: (event) => {
|
|
527
|
+
if (event.target.files?.length)
|
|
528
|
+
deliverFiles(Array.from(event.target.files));
|
|
529
|
+
event.target.value = "";
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
)
|
|
533
|
+
] }),
|
|
534
|
+
onAttachFolder && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
535
|
+
/* @__PURE__ */ jsx2(
|
|
536
|
+
"button",
|
|
537
|
+
{
|
|
538
|
+
type: "button",
|
|
539
|
+
onClick: () => folderInputRef.current?.click(),
|
|
540
|
+
disabled,
|
|
541
|
+
"aria-label": "Attach folder",
|
|
542
|
+
title: "Attach folder",
|
|
543
|
+
className: attachButtonClass,
|
|
544
|
+
children: /* @__PURE__ */ jsx2(Folder, { className: "size-4" })
|
|
545
|
+
}
|
|
546
|
+
),
|
|
547
|
+
/* @__PURE__ */ jsx2(
|
|
548
|
+
"input",
|
|
549
|
+
{
|
|
550
|
+
ref: folderInputRef,
|
|
551
|
+
type: "file",
|
|
552
|
+
multiple: true,
|
|
553
|
+
className: "hidden",
|
|
554
|
+
onChange: (event) => {
|
|
555
|
+
if (event.target.files?.length)
|
|
556
|
+
(onAttachFolder ?? onAttach)?.(event.target.files);
|
|
557
|
+
event.target.value = "";
|
|
558
|
+
},
|
|
559
|
+
...{ webkitdirectory: "" }
|
|
560
|
+
}
|
|
561
|
+
)
|
|
562
|
+
] }),
|
|
563
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-1 items-center gap-2 overflow-x-auto overscroll-x-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [
|
|
564
|
+
controls,
|
|
565
|
+
trailing && /* @__PURE__ */ jsx2("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: trailing })
|
|
566
|
+
] }),
|
|
567
|
+
busy && onCancel ? /* @__PURE__ */ jsx2(
|
|
568
|
+
"button",
|
|
569
|
+
{
|
|
570
|
+
type: "button",
|
|
571
|
+
onClick: onCancel,
|
|
572
|
+
"aria-label": "Stop response",
|
|
573
|
+
className: cn(
|
|
574
|
+
"flex size-9 shrink-0 items-center justify-center rounded-full",
|
|
575
|
+
"bg-[var(--status-error,#ff4d6d)]/15 text-[var(--status-error,#ff4d6d)] transition-colors",
|
|
576
|
+
"hover:bg-[var(--status-error,#ff4d6d)]/25"
|
|
577
|
+
),
|
|
578
|
+
children: /* @__PURE__ */ jsx2(Square, { className: "size-3.5 fill-current" })
|
|
579
|
+
}
|
|
580
|
+
) : /* @__PURE__ */ jsx2(
|
|
581
|
+
"button",
|
|
582
|
+
{
|
|
583
|
+
type: "button",
|
|
584
|
+
onClick: submit,
|
|
585
|
+
disabled: !canSend,
|
|
586
|
+
"aria-label": sendLabel,
|
|
587
|
+
className: cn(
|
|
588
|
+
"flex size-9 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground",
|
|
589
|
+
"transition-opacity hover:opacity-90 disabled:opacity-40"
|
|
590
|
+
),
|
|
591
|
+
children: busy ? /* @__PURE__ */ jsx2(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx2(ArrowUp, { className: "size-4" })
|
|
592
|
+
}
|
|
593
|
+
)
|
|
594
|
+
] })
|
|
595
|
+
]
|
|
596
|
+
}
|
|
597
|
+
),
|
|
598
|
+
focusShortcut && /* @__PURE__ */ jsx2("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs2("span", { className: "text-muted-foreground text-xs", children: [
|
|
599
|
+
/* @__PURE__ */ jsx2("kbd", { className: "rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "Cmd" }),
|
|
600
|
+
/* @__PURE__ */ jsx2("kbd", { className: "ml-0.5 rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "L" }),
|
|
601
|
+
/* @__PURE__ */ jsx2("span", { className: "ml-1", children: "to focus" })
|
|
602
|
+
] }) })
|
|
603
|
+
] });
|
|
1275
604
|
}
|
|
1276
605
|
|
|
1277
|
-
// src/chat/agent-
|
|
1278
|
-
import {
|
|
1279
|
-
|
|
1280
|
-
Folder,
|
|
1281
|
-
Loader2,
|
|
1282
|
-
Paperclip,
|
|
1283
|
-
RotateCcw,
|
|
1284
|
-
Square,
|
|
1285
|
-
Upload,
|
|
1286
|
-
X
|
|
1287
|
-
} from "lucide-react";
|
|
1288
|
-
import * as React6 from "react";
|
|
606
|
+
// src/chat/agent-profile-picker.tsx
|
|
607
|
+
import { Bot, Check, ChevronDown as ChevronDown2, Pencil, Plus as Plus2, Trash2 } from "lucide-react";
|
|
608
|
+
import * as React5 from "react";
|
|
1289
609
|
|
|
1290
|
-
// src/
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
if (lowerPattern.endsWith("/*")) {
|
|
1303
|
-
const prefix = lowerPattern.slice(0, -1);
|
|
1304
|
-
return type.startsWith(prefix);
|
|
610
|
+
// src/lib/use-click-outside.ts
|
|
611
|
+
import * as React2 from "react";
|
|
612
|
+
function useClickOutside(onOutside, extras) {
|
|
613
|
+
const ref = React2.useRef(null);
|
|
614
|
+
const extrasRef = React2.useRef(extras);
|
|
615
|
+
extrasRef.current = extras;
|
|
616
|
+
React2.useEffect(() => {
|
|
617
|
+
function handler(event) {
|
|
618
|
+
const target = event.target;
|
|
619
|
+
if (!ref.current || ref.current.contains(target)) return;
|
|
620
|
+
if (extrasRef.current?.some((extra) => extra.current?.contains(target))) return;
|
|
621
|
+
onOutside();
|
|
1305
622
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
623
|
+
document.addEventListener("mousedown", handler);
|
|
624
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
625
|
+
}, [onOutside]);
|
|
626
|
+
return ref;
|
|
1308
627
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
628
|
+
|
|
629
|
+
// src/chat/anchored-popover.tsx
|
|
630
|
+
import * as React3 from "react";
|
|
631
|
+
import { createPortal } from "react-dom";
|
|
632
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
633
|
+
var AnchoredPopover = React3.forwardRef(
|
|
634
|
+
function AnchoredPopover2({ anchorRef, side, children, className, style, ...rest }, forwardedRef) {
|
|
635
|
+
const innerRef = React3.useRef(null);
|
|
636
|
+
const setRefs = (node) => {
|
|
637
|
+
innerRef.current = node;
|
|
638
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
639
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
640
|
+
};
|
|
641
|
+
const [position, setPosition] = React3.useState(null);
|
|
642
|
+
const place = React3.useCallback(() => {
|
|
643
|
+
const anchor = anchorRef.current;
|
|
644
|
+
if (!anchor) return;
|
|
645
|
+
const rect = anchor.getBoundingClientRect();
|
|
646
|
+
const width = innerRef.current?.offsetWidth ?? 0;
|
|
647
|
+
const margin = 8;
|
|
648
|
+
const left = Math.max(
|
|
649
|
+
margin,
|
|
650
|
+
Math.min(rect.left, window.innerWidth - width - margin)
|
|
651
|
+
);
|
|
652
|
+
setPosition(
|
|
653
|
+
side === "top" ? { left, bottom: window.innerHeight - rect.top + margin } : { left, top: rect.bottom + margin }
|
|
654
|
+
);
|
|
655
|
+
}, [anchorRef, side]);
|
|
656
|
+
React3.useLayoutEffect(() => {
|
|
657
|
+
place();
|
|
658
|
+
window.addEventListener("resize", place);
|
|
659
|
+
window.addEventListener("scroll", place, true);
|
|
660
|
+
return () => {
|
|
661
|
+
window.removeEventListener("resize", place);
|
|
662
|
+
window.removeEventListener("scroll", place, true);
|
|
663
|
+
};
|
|
664
|
+
}, [place]);
|
|
665
|
+
return createPortal(
|
|
666
|
+
/* @__PURE__ */ jsx3(
|
|
667
|
+
"div",
|
|
668
|
+
{
|
|
669
|
+
...rest,
|
|
670
|
+
ref: setRefs,
|
|
671
|
+
className: cn("fixed z-50", className),
|
|
672
|
+
style: {
|
|
673
|
+
...style,
|
|
674
|
+
pointerEvents: "auto",
|
|
675
|
+
...position ?? { visibility: "hidden", left: 0, top: 0 }
|
|
676
|
+
},
|
|
677
|
+
children
|
|
678
|
+
}
|
|
679
|
+
),
|
|
680
|
+
document.body
|
|
681
|
+
);
|
|
1337
682
|
}
|
|
1338
|
-
|
|
1339
|
-
}
|
|
683
|
+
);
|
|
1340
684
|
|
|
1341
|
-
// src/chat/
|
|
1342
|
-
import {
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
function isGenericImageName(name) {
|
|
1353
|
-
return /^(image)?(\.[a-z0-9]+)?$/i.test(name.trim());
|
|
1354
|
-
}
|
|
1355
|
-
function imageExtension(file) {
|
|
1356
|
-
const fromMime = IMAGE_EXTENSION_BY_MIME[file.type.toLowerCase()];
|
|
1357
|
-
if (fromMime) return fromMime;
|
|
1358
|
-
const fromName = /\.([a-z0-9]+)$/i.exec(file.name)?.[1];
|
|
1359
|
-
return fromName?.toLowerCase() ?? "png";
|
|
1360
|
-
}
|
|
1361
|
-
var MentionEditor = React6.lazy(() => import("./mention-editor-3Q6MYOIU.js"));
|
|
1362
|
-
function AgentComposer({
|
|
1363
|
-
value,
|
|
1364
|
-
onChange,
|
|
1365
|
-
onSubmit,
|
|
1366
|
-
placeholder = "Send a message\u2026",
|
|
1367
|
-
disabled,
|
|
1368
|
-
busy,
|
|
1369
|
-
onCancel,
|
|
1370
|
-
harness,
|
|
1371
|
-
profile,
|
|
1372
|
-
model,
|
|
1373
|
-
reasoning,
|
|
1374
|
-
context = "chat",
|
|
1375
|
-
controls,
|
|
1376
|
-
trailing,
|
|
1377
|
-
onAttach,
|
|
1378
|
-
onAttachFolder,
|
|
1379
|
-
attachments = [],
|
|
1380
|
-
onRemoveFile,
|
|
1381
|
-
onRetryFile,
|
|
1382
|
-
accept,
|
|
1383
|
-
onRejectFiles,
|
|
1384
|
-
dropTitle = "Drop files to add context",
|
|
1385
|
-
dropDescription = "They attach to your next message.",
|
|
1386
|
-
canSubmitWhileBusy = false,
|
|
1387
|
-
canSubmitAttachmentsOnly = false,
|
|
1388
|
-
mention,
|
|
1389
|
-
focusShortcut = false,
|
|
1390
|
-
minRows = 2,
|
|
1391
|
-
maxHeight = 200,
|
|
685
|
+
// src/chat/informative-lock.tsx
|
|
686
|
+
import { Lock, Plus } from "lucide-react";
|
|
687
|
+
import * as React4 from "react";
|
|
688
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
689
|
+
function InformativeLock({
|
|
690
|
+
children,
|
|
691
|
+
ariaLabel,
|
|
692
|
+
lockTitle,
|
|
693
|
+
lockBody,
|
|
694
|
+
newChatLabel,
|
|
695
|
+
onNewChat,
|
|
1392
696
|
className,
|
|
1393
|
-
|
|
1394
|
-
|
|
697
|
+
triggerClassName,
|
|
698
|
+
popoverClassName,
|
|
699
|
+
side = "top"
|
|
1395
700
|
}) {
|
|
1396
|
-
const
|
|
1397
|
-
const
|
|
1398
|
-
const
|
|
1399
|
-
const
|
|
1400
|
-
const
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
(el) => {
|
|
1405
|
-
el.style.height = "auto";
|
|
1406
|
-
el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`;
|
|
1407
|
-
},
|
|
1408
|
-
[maxHeight]
|
|
1409
|
-
);
|
|
1410
|
-
React6.useEffect(() => {
|
|
1411
|
-
if (textareaRef.current) resize(textareaRef.current);
|
|
1412
|
-
}, [value, resize]);
|
|
1413
|
-
React6.useEffect(() => {
|
|
1414
|
-
if (!focusShortcut || disabled) return;
|
|
1415
|
-
function onKeyDown(event) {
|
|
1416
|
-
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "l") {
|
|
1417
|
-
event.preventDefault();
|
|
1418
|
-
if (mention) richFocusRef.current?.();
|
|
1419
|
-
else textareaRef.current?.focus();
|
|
1420
|
-
}
|
|
701
|
+
const [open, setOpen] = React4.useState(false);
|
|
702
|
+
const closeTimer = React4.useRef(null);
|
|
703
|
+
const popoverRef = React4.useRef(null);
|
|
704
|
+
const ref = useClickOutside(() => setOpen(false), [popoverRef]);
|
|
705
|
+
const cancelClose = React4.useCallback(() => {
|
|
706
|
+
if (closeTimer.current) {
|
|
707
|
+
clearTimeout(closeTimer.current);
|
|
708
|
+
closeTimer.current = null;
|
|
1421
709
|
}
|
|
1422
|
-
document.addEventListener("keydown", onKeyDown);
|
|
1423
|
-
return () => document.removeEventListener("keydown", onKeyDown);
|
|
1424
|
-
}, [focusShortcut, disabled, mention]);
|
|
1425
|
-
const canSend = !disabled && (!busy || canSubmitWhileBusy) && (value.trim().length > 0 || canSubmitAttachmentsOnly && attachments.length > 0);
|
|
1426
|
-
const submit = () => {
|
|
1427
|
-
if (!canSend) return;
|
|
1428
|
-
onSubmit();
|
|
1429
|
-
};
|
|
1430
|
-
const registerRichFocus = React6.useCallback((focus) => {
|
|
1431
|
-
richFocusRef.current = focus;
|
|
1432
710
|
}, []);
|
|
1433
|
-
const
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
711
|
+
const scheduleClose = React4.useCallback(() => {
|
|
712
|
+
cancelClose();
|
|
713
|
+
closeTimer.current = setTimeout(() => setOpen(false), 150);
|
|
714
|
+
}, [cancelClose]);
|
|
715
|
+
React4.useEffect(() => cancelClose, [cancelClose]);
|
|
716
|
+
return /* @__PURE__ */ jsxs3(
|
|
717
|
+
"div",
|
|
718
|
+
{
|
|
719
|
+
ref,
|
|
720
|
+
className: cn("relative inline-flex", className),
|
|
721
|
+
onMouseEnter: () => {
|
|
722
|
+
cancelClose();
|
|
723
|
+
setOpen(true);
|
|
724
|
+
},
|
|
725
|
+
onMouseLeave: scheduleClose,
|
|
726
|
+
onKeyDown: (event) => {
|
|
727
|
+
if (event.key === "Escape") setOpen(false);
|
|
728
|
+
},
|
|
729
|
+
children: [
|
|
730
|
+
/* @__PURE__ */ jsx4(
|
|
731
|
+
"button",
|
|
732
|
+
{
|
|
733
|
+
type: "button",
|
|
734
|
+
onClick: () => setOpen(true),
|
|
735
|
+
"data-state": open ? "open" : "closed",
|
|
736
|
+
"aria-label": ariaLabel,
|
|
737
|
+
"aria-haspopup": "dialog",
|
|
738
|
+
"aria-expanded": open,
|
|
739
|
+
className: cn(
|
|
740
|
+
"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",
|
|
741
|
+
"text-xs font-medium text-foreground shadow-sm transition-colors",
|
|
742
|
+
"hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
743
|
+
"data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
|
|
744
|
+
triggerClassName
|
|
745
|
+
),
|
|
746
|
+
children
|
|
747
|
+
}
|
|
748
|
+
),
|
|
749
|
+
open && /* @__PURE__ */ jsxs3(
|
|
750
|
+
AnchoredPopover,
|
|
751
|
+
{
|
|
752
|
+
ref: popoverRef,
|
|
753
|
+
anchorRef: ref,
|
|
754
|
+
side,
|
|
755
|
+
role: "dialog",
|
|
756
|
+
onMouseEnter: () => {
|
|
757
|
+
cancelClose();
|
|
758
|
+
setOpen(true);
|
|
759
|
+
},
|
|
760
|
+
onMouseLeave: scheduleClose,
|
|
761
|
+
className: cn(
|
|
762
|
+
"w-72 rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-3",
|
|
763
|
+
"shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
|
|
764
|
+
popoverClassName
|
|
765
|
+
),
|
|
766
|
+
children: [
|
|
767
|
+
/* @__PURE__ */ jsxs3("p", { className: "flex items-start gap-2 text-sm font-medium text-foreground", children: [
|
|
768
|
+
/* @__PURE__ */ jsx4(Lock, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
769
|
+
/* @__PURE__ */ jsx4("span", { children: lockTitle })
|
|
770
|
+
] }),
|
|
771
|
+
/* @__PURE__ */ jsx4("p", { className: "mt-1.5 text-xs text-muted-foreground", children: lockBody }),
|
|
772
|
+
/* @__PURE__ */ jsxs3(
|
|
773
|
+
"button",
|
|
774
|
+
{
|
|
775
|
+
type: "button",
|
|
776
|
+
onClick: () => {
|
|
777
|
+
setOpen(false);
|
|
778
|
+
onNewChat();
|
|
779
|
+
},
|
|
780
|
+
className: cn(
|
|
781
|
+
"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",
|
|
782
|
+
"bg-primary/10 ring-1 ring-inset ring-primary/25 transition-colors",
|
|
783
|
+
"hover:bg-primary/15 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
784
|
+
),
|
|
785
|
+
children: [
|
|
786
|
+
/* @__PURE__ */ jsx4(Plus, { className: "h-4 w-4" }),
|
|
787
|
+
newChatLabel
|
|
788
|
+
]
|
|
789
|
+
}
|
|
790
|
+
)
|
|
791
|
+
]
|
|
792
|
+
}
|
|
793
|
+
)
|
|
794
|
+
]
|
|
1483
795
|
}
|
|
1484
|
-
return file;
|
|
1485
|
-
});
|
|
1486
|
-
const handlePaste = (event) => {
|
|
1487
|
-
if (!onAttach) return;
|
|
1488
|
-
const clipboardFiles = event.clipboardData?.files;
|
|
1489
|
-
if (!clipboardFiles || clipboardFiles.length === 0) return;
|
|
1490
|
-
event.preventDefault();
|
|
1491
|
-
deliverFiles(renamePastedFiles(Array.from(clipboardFiles)));
|
|
1492
|
-
};
|
|
1493
|
-
const handleEditorPasteFiles = (clipboardFiles) => {
|
|
1494
|
-
if (!onAttach || clipboardFiles.length === 0) return false;
|
|
1495
|
-
deliverFiles(renamePastedFiles(Array.from(clipboardFiles)));
|
|
1496
|
-
return true;
|
|
1497
|
-
};
|
|
1498
|
-
const folderChips = attachments.filter((file) => file.kind === "folder");
|
|
1499
|
-
const fileChips = attachments.filter((file) => file.kind !== "folder");
|
|
1500
|
-
const attachButtonClass = cn(
|
|
1501
|
-
"flex size-8 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors",
|
|
1502
|
-
"hover:bg-surface-container-highest hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
1503
|
-
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
1504
796
|
);
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// src/chat/agent-profile-picker.tsx
|
|
800
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
801
|
+
function profileSubtitle(profile) {
|
|
802
|
+
if (profile.description) return profile.description;
|
|
803
|
+
const count = profile.capabilities?.length ?? 0;
|
|
804
|
+
return `${count} ${count === 1 ? "tool group" : "tool groups"}`;
|
|
805
|
+
}
|
|
806
|
+
function AgentProfilePicker({
|
|
807
|
+
value,
|
|
808
|
+
onChange,
|
|
809
|
+
profiles,
|
|
810
|
+
capabilities,
|
|
811
|
+
onCreate,
|
|
812
|
+
onUpdate,
|
|
813
|
+
onDelete,
|
|
814
|
+
disabled,
|
|
815
|
+
locked,
|
|
816
|
+
lockReason,
|
|
817
|
+
onNewChat,
|
|
818
|
+
className,
|
|
819
|
+
triggerClassName,
|
|
820
|
+
popoverClassName,
|
|
821
|
+
side = "top"
|
|
822
|
+
}) {
|
|
823
|
+
const [open, setOpen] = React5.useState(false);
|
|
824
|
+
const [composing, setComposing] = React5.useState(
|
|
825
|
+
null
|
|
826
|
+
);
|
|
827
|
+
const popoverRef = React5.useRef(null);
|
|
828
|
+
const ref = useClickOutside(() => {
|
|
829
|
+
setOpen(false);
|
|
830
|
+
setComposing(null);
|
|
831
|
+
}, [popoverRef]);
|
|
832
|
+
const canAuthor = Boolean(capabilities && (onCreate || onUpdate));
|
|
833
|
+
const builtins = profiles.filter((profile) => profile.builtin);
|
|
834
|
+
const custom = profiles.filter((profile) => !profile.builtin);
|
|
835
|
+
const selected = profiles.find((profile) => profile.id === value);
|
|
836
|
+
const selectedName = selected?.name ?? value;
|
|
837
|
+
React5.useEffect(() => {
|
|
838
|
+
if (!locked) return;
|
|
839
|
+
setOpen(false);
|
|
840
|
+
setComposing(null);
|
|
841
|
+
}, [locked]);
|
|
842
|
+
function select(id) {
|
|
843
|
+
onChange(id);
|
|
844
|
+
setOpen(false);
|
|
845
|
+
}
|
|
846
|
+
async function save(draft) {
|
|
847
|
+
if (draft.id) await onUpdate?.(draft.id, draft);
|
|
848
|
+
else await onCreate?.(draft);
|
|
849
|
+
setComposing(null);
|
|
850
|
+
}
|
|
851
|
+
if (locked && onNewChat) {
|
|
852
|
+
return /* @__PURE__ */ jsxs4(
|
|
853
|
+
InformativeLock,
|
|
1513
854
|
{
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
855
|
+
ariaLabel: "Agent profile (locked)",
|
|
856
|
+
lockTitle: `Profile fixed to ${selectedName} for this conversation.`,
|
|
857
|
+
lockBody: "Conversations stay on one profile.",
|
|
858
|
+
newChatLabel: "New chat to switch profile",
|
|
859
|
+
onNewChat,
|
|
860
|
+
className,
|
|
861
|
+
triggerClassName,
|
|
862
|
+
popoverClassName,
|
|
863
|
+
side,
|
|
864
|
+
children: [
|
|
865
|
+
/* @__PURE__ */ jsx5(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
866
|
+
/* @__PURE__ */ jsx5("span", { className: "max-w-[140px] truncate", children: selectedName })
|
|
867
|
+
]
|
|
868
|
+
}
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
return /* @__PURE__ */ jsxs4("div", { ref, className: cn("relative inline-flex", className), children: [
|
|
872
|
+
/* @__PURE__ */ jsxs4(
|
|
873
|
+
"button",
|
|
874
|
+
{
|
|
875
|
+
type: "button",
|
|
876
|
+
disabled: disabled || locked,
|
|
877
|
+
title: locked ? lockReason : void 0,
|
|
878
|
+
onClick: () => setOpen((v) => !v),
|
|
1519
879
|
className: cn(
|
|
1520
|
-
"flex
|
|
1521
|
-
"
|
|
1522
|
-
|
|
880
|
+
"inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5",
|
|
881
|
+
"text-xs font-medium text-foreground shadow-sm transition-colors",
|
|
882
|
+
"hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
883
|
+
"data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
|
|
884
|
+
"disabled:cursor-not-allowed disabled:opacity-60",
|
|
885
|
+
triggerClassName
|
|
1523
886
|
),
|
|
887
|
+
"data-state": open ? "open" : "closed",
|
|
888
|
+
"aria-label": "Agent profile",
|
|
1524
889
|
children: [
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
"button",
|
|
1556
|
-
{
|
|
1557
|
-
type: "button",
|
|
1558
|
-
"aria-label": `Retry upload ${file.name}`,
|
|
1559
|
-
onClick: () => onRetryFile(file.id),
|
|
1560
|
-
className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
1561
|
-
children: /* @__PURE__ */ jsx6(RotateCcw, { className: "size-3" })
|
|
1562
|
-
}
|
|
1563
|
-
),
|
|
1564
|
-
onRemoveFile && /* @__PURE__ */ jsx6(
|
|
1565
|
-
"button",
|
|
1566
|
-
{
|
|
1567
|
-
type: "button",
|
|
1568
|
-
"aria-label": `Remove ${file.name}`,
|
|
1569
|
-
onClick: () => onRemoveFile(file.id),
|
|
1570
|
-
className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
1571
|
-
children: /* @__PURE__ */ jsx6(X, { className: "size-3" })
|
|
1572
|
-
}
|
|
1573
|
-
)
|
|
1574
|
-
]
|
|
1575
|
-
},
|
|
1576
|
-
file.id
|
|
1577
|
-
);
|
|
1578
|
-
}) }),
|
|
1579
|
-
mention ? /* @__PURE__ */ jsx6(
|
|
1580
|
-
React6.Suspense,
|
|
890
|
+
/* @__PURE__ */ jsx5(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
891
|
+
/* @__PURE__ */ jsx5("span", { className: "max-w-[140px] truncate", children: selectedName }),
|
|
892
|
+
!locked && /* @__PURE__ */ jsx5(ChevronDown2, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
|
|
893
|
+
]
|
|
894
|
+
}
|
|
895
|
+
),
|
|
896
|
+
open && !locked && /* @__PURE__ */ jsx5(
|
|
897
|
+
AnchoredPopover,
|
|
898
|
+
{
|
|
899
|
+
ref: popoverRef,
|
|
900
|
+
anchorRef: ref,
|
|
901
|
+
side,
|
|
902
|
+
className: cn(
|
|
903
|
+
"w-[min(320px,calc(100vw-2rem))] overflow-hidden rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-1",
|
|
904
|
+
"shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
|
|
905
|
+
popoverClassName
|
|
906
|
+
),
|
|
907
|
+
"data-testid": "agent-profile-popover",
|
|
908
|
+
children: composing ? /* @__PURE__ */ jsx5(
|
|
909
|
+
ProfileForm,
|
|
910
|
+
{
|
|
911
|
+
capabilities: capabilities ?? [],
|
|
912
|
+
initial: composing === "new" ? void 0 : composing,
|
|
913
|
+
onCancel: () => setComposing(null),
|
|
914
|
+
onSave: save
|
|
915
|
+
}
|
|
916
|
+
) : /* @__PURE__ */ jsxs4("div", { className: "max-h-[min(60vh,420px)] overflow-y-auto", children: [
|
|
917
|
+
/* @__PURE__ */ jsx5(SectionHeader, { children: "Modes" }),
|
|
918
|
+
builtins.map((profile) => /* @__PURE__ */ jsx5(
|
|
919
|
+
ProfileRow,
|
|
1581
920
|
{
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
),
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
921
|
+
active: profile.id === value,
|
|
922
|
+
title: profile.name,
|
|
923
|
+
subtitle: profileSubtitle(profile),
|
|
924
|
+
onSelect: () => select(profile.id)
|
|
925
|
+
},
|
|
926
|
+
profile.id
|
|
927
|
+
)),
|
|
928
|
+
custom.length > 0 && /* @__PURE__ */ jsx5(SectionHeader, { children: "Your agents" }),
|
|
929
|
+
custom.map((profile) => /* @__PURE__ */ jsx5(
|
|
930
|
+
ProfileRow,
|
|
931
|
+
{
|
|
932
|
+
active: profile.id === value,
|
|
933
|
+
title: profile.name,
|
|
934
|
+
subtitle: profileSubtitle(profile),
|
|
935
|
+
onSelect: () => select(profile.id),
|
|
936
|
+
onEdit: canAuthor ? () => setComposing(profile) : void 0,
|
|
937
|
+
onDelete: onDelete ? () => void onDelete(profile.id) : void 0
|
|
938
|
+
},
|
|
939
|
+
profile.id
|
|
940
|
+
)),
|
|
941
|
+
canAuthor && /* @__PURE__ */ jsxs4(
|
|
942
|
+
"button",
|
|
943
|
+
{
|
|
944
|
+
type: "button",
|
|
945
|
+
onClick: () => setComposing("new"),
|
|
946
|
+
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",
|
|
947
|
+
children: [
|
|
948
|
+
/* @__PURE__ */ jsx5(Plus2, { className: "h-4 w-4" }),
|
|
949
|
+
" New agent\u2026"
|
|
950
|
+
]
|
|
1612
951
|
}
|
|
1613
|
-
)
|
|
1614
|
-
|
|
952
|
+
)
|
|
953
|
+
] })
|
|
954
|
+
}
|
|
955
|
+
)
|
|
956
|
+
] });
|
|
957
|
+
}
|
|
958
|
+
function SectionHeader({ children }) {
|
|
959
|
+
return /* @__PURE__ */ jsx5("div", { className: "px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children });
|
|
960
|
+
}
|
|
961
|
+
function ProfileRow({
|
|
962
|
+
active,
|
|
963
|
+
title,
|
|
964
|
+
subtitle,
|
|
965
|
+
onSelect,
|
|
966
|
+
onEdit,
|
|
967
|
+
onDelete
|
|
968
|
+
}) {
|
|
969
|
+
return /* @__PURE__ */ jsxs4(
|
|
970
|
+
"div",
|
|
971
|
+
{
|
|
972
|
+
className: cn(
|
|
973
|
+
"group flex items-center gap-2 rounded-md px-2.5 py-2 text-sm transition-colors",
|
|
974
|
+
active ? "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]" : "hover:bg-accent/40"
|
|
975
|
+
),
|
|
976
|
+
children: [
|
|
977
|
+
/* @__PURE__ */ jsxs4(
|
|
978
|
+
"button",
|
|
979
|
+
{
|
|
980
|
+
type: "button",
|
|
981
|
+
onClick: onSelect,
|
|
982
|
+
className: "flex min-w-0 flex-1 items-center gap-2 text-left",
|
|
983
|
+
children: [
|
|
984
|
+
/* @__PURE__ */ jsx5("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: active && /* @__PURE__ */ jsx5(Check, { className: "h-3.5 w-3.5" }) }),
|
|
985
|
+
/* @__PURE__ */ jsxs4("span", { className: "min-w-0", children: [
|
|
986
|
+
/* @__PURE__ */ jsx5("span", { className: "block truncate font-medium", children: title }),
|
|
987
|
+
/* @__PURE__ */ jsx5("span", { className: "block truncate text-xs text-muted-foreground", children: subtitle })
|
|
988
|
+
] })
|
|
989
|
+
]
|
|
990
|
+
}
|
|
991
|
+
),
|
|
992
|
+
(onEdit || onDelete) && /* @__PURE__ */ jsxs4("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100", children: [
|
|
993
|
+
onEdit && /* @__PURE__ */ jsx5(
|
|
994
|
+
"button",
|
|
1615
995
|
{
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
onChange: (event) => onChange(event.target.value),
|
|
1622
|
-
onInput: (event) => resize(event.currentTarget),
|
|
1623
|
-
onPaste: handlePaste,
|
|
1624
|
-
onKeyDown: (event) => {
|
|
1625
|
-
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing && event.keyCode !== 229) {
|
|
1626
|
-
event.preventDefault();
|
|
1627
|
-
submit();
|
|
1628
|
-
}
|
|
1629
|
-
},
|
|
1630
|
-
placeholder,
|
|
1631
|
-
"aria-label": "Message input",
|
|
1632
|
-
className: cn(
|
|
1633
|
-
"w-full resize-none bg-transparent px-2 py-1 text-sm leading-relaxed text-foreground outline-none",
|
|
1634
|
-
"placeholder:text-muted-foreground"
|
|
1635
|
-
)
|
|
996
|
+
type: "button",
|
|
997
|
+
onClick: onEdit,
|
|
998
|
+
title: "Edit",
|
|
999
|
+
className: "rounded p-1 text-muted-foreground hover:text-foreground",
|
|
1000
|
+
children: /* @__PURE__ */ jsx5(Pencil, { className: "h-3 w-3" })
|
|
1636
1001
|
}
|
|
1637
1002
|
),
|
|
1638
|
-
/* @__PURE__ */
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
className: "hidden",
|
|
1688
|
-
onChange: (event) => {
|
|
1689
|
-
if (event.target.files?.length)
|
|
1690
|
-
(onAttachFolder ?? onAttach)?.(event.target.files);
|
|
1691
|
-
event.target.value = "";
|
|
1692
|
-
},
|
|
1693
|
-
...{ webkitdirectory: "" }
|
|
1694
|
-
}
|
|
1695
|
-
)
|
|
1696
|
-
] }),
|
|
1697
|
-
/* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-1 items-center gap-2 overflow-x-auto overscroll-x-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [
|
|
1698
|
-
controls ?? /* @__PURE__ */ jsx6(
|
|
1699
|
-
AgentSessionControls,
|
|
1700
|
-
{
|
|
1701
|
-
context,
|
|
1702
|
-
harness,
|
|
1703
|
-
profile,
|
|
1704
|
-
model,
|
|
1705
|
-
reasoning
|
|
1706
|
-
}
|
|
1707
|
-
),
|
|
1708
|
-
trailing && /* @__PURE__ */ jsx6("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: trailing })
|
|
1709
|
-
] }),
|
|
1710
|
-
busy && onCancel ? /* @__PURE__ */ jsx6(
|
|
1711
|
-
"button",
|
|
1712
|
-
{
|
|
1713
|
-
type: "button",
|
|
1714
|
-
onClick: onCancel,
|
|
1715
|
-
"aria-label": "Stop response",
|
|
1716
|
-
className: cn(
|
|
1717
|
-
"flex size-9 shrink-0 items-center justify-center rounded-full",
|
|
1718
|
-
"bg-[var(--status-error,#ff4d6d)]/15 text-[var(--status-error,#ff4d6d)] transition-colors",
|
|
1719
|
-
"hover:bg-[var(--status-error,#ff4d6d)]/25"
|
|
1720
|
-
),
|
|
1721
|
-
children: /* @__PURE__ */ jsx6(Square, { className: "size-3.5 fill-current" })
|
|
1722
|
-
}
|
|
1723
|
-
) : /* @__PURE__ */ jsx6(
|
|
1724
|
-
"button",
|
|
1003
|
+
onDelete && /* @__PURE__ */ jsx5(
|
|
1004
|
+
"button",
|
|
1005
|
+
{
|
|
1006
|
+
type: "button",
|
|
1007
|
+
onClick: onDelete,
|
|
1008
|
+
title: "Delete",
|
|
1009
|
+
className: "rounded p-1 text-muted-foreground hover:text-[var(--status-error,#ff4d6d)]",
|
|
1010
|
+
children: /* @__PURE__ */ jsx5(Trash2, { className: "h-3 w-3" })
|
|
1011
|
+
}
|
|
1012
|
+
)
|
|
1013
|
+
] })
|
|
1014
|
+
]
|
|
1015
|
+
}
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
function ProfileForm({
|
|
1019
|
+
capabilities,
|
|
1020
|
+
initial,
|
|
1021
|
+
onCancel,
|
|
1022
|
+
onSave
|
|
1023
|
+
}) {
|
|
1024
|
+
const [name, setName] = React5.useState(initial?.name ?? "");
|
|
1025
|
+
const [caps, setCaps] = React5.useState(
|
|
1026
|
+
initial?.capabilities ? [...initial.capabilities] : capabilities.map((c) => c.id)
|
|
1027
|
+
);
|
|
1028
|
+
const [instructions, setInstructions] = React5.useState(initial?.instructions ?? "");
|
|
1029
|
+
const toggle = (id) => setCaps(
|
|
1030
|
+
(prev) => prev.includes(id) ? prev.filter((c) => c !== id) : [...prev, id]
|
|
1031
|
+
);
|
|
1032
|
+
return /* @__PURE__ */ jsxs4("div", { className: "space-y-3 p-2.5", children: [
|
|
1033
|
+
/* @__PURE__ */ jsx5(
|
|
1034
|
+
"input",
|
|
1035
|
+
{
|
|
1036
|
+
autoFocus: true,
|
|
1037
|
+
value: name,
|
|
1038
|
+
onChange: (event) => setName(event.target.value),
|
|
1039
|
+
placeholder: "Agent name (e.g. Tax Reviewer)",
|
|
1040
|
+
className: "w-full rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
|
|
1041
|
+
}
|
|
1042
|
+
),
|
|
1043
|
+
capabilities.length > 0 && /* @__PURE__ */ jsxs4("div", { children: [
|
|
1044
|
+
/* @__PURE__ */ jsx5("p", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children: "Tools" }),
|
|
1045
|
+
/* @__PURE__ */ jsx5("div", { className: "space-y-0.5", children: capabilities.map((cap) => /* @__PURE__ */ jsxs4(
|
|
1046
|
+
"label",
|
|
1047
|
+
{
|
|
1048
|
+
className: "flex cursor-pointer items-start gap-2 rounded-md px-2 py-1.5 hover:bg-accent/40",
|
|
1049
|
+
children: [
|
|
1050
|
+
/* @__PURE__ */ jsx5(
|
|
1051
|
+
"input",
|
|
1725
1052
|
{
|
|
1726
|
-
type: "
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
"
|
|
1730
|
-
className: cn(
|
|
1731
|
-
"flex size-9 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground",
|
|
1732
|
-
"transition-opacity hover:opacity-90 disabled:opacity-40"
|
|
1733
|
-
),
|
|
1734
|
-
children: busy ? /* @__PURE__ */ jsx6(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx6(ArrowUp, { className: "size-4" })
|
|
1053
|
+
type: "checkbox",
|
|
1054
|
+
checked: caps.includes(cap.id),
|
|
1055
|
+
onChange: () => toggle(cap.id),
|
|
1056
|
+
className: "mt-0.5"
|
|
1735
1057
|
}
|
|
1736
|
-
)
|
|
1737
|
-
|
|
1738
|
-
|
|
1058
|
+
),
|
|
1059
|
+
/* @__PURE__ */ jsxs4("span", { className: "min-w-0", children: [
|
|
1060
|
+
/* @__PURE__ */ jsx5("span", { className: "block text-sm font-medium", children: cap.label }),
|
|
1061
|
+
cap.description && /* @__PURE__ */ jsx5("span", { className: "block text-xs text-muted-foreground", children: cap.description })
|
|
1062
|
+
] })
|
|
1063
|
+
]
|
|
1064
|
+
},
|
|
1065
|
+
cap.id
|
|
1066
|
+
)) })
|
|
1067
|
+
] }),
|
|
1068
|
+
/* @__PURE__ */ jsx5(
|
|
1069
|
+
"textarea",
|
|
1070
|
+
{
|
|
1071
|
+
value: instructions,
|
|
1072
|
+
onChange: (event) => setInstructions(event.target.value),
|
|
1073
|
+
rows: 2,
|
|
1074
|
+
placeholder: "Instructions (optional) \u2014 how this agent should behave",
|
|
1075
|
+
className: "w-full resize-none rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
|
|
1739
1076
|
}
|
|
1740
1077
|
),
|
|
1741
|
-
|
|
1742
|
-
/* @__PURE__ */
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1078
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex justify-end gap-2", children: [
|
|
1079
|
+
/* @__PURE__ */ jsx5(
|
|
1080
|
+
"button",
|
|
1081
|
+
{
|
|
1082
|
+
type: "button",
|
|
1083
|
+
onClick: onCancel,
|
|
1084
|
+
className: "rounded-lg px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent/40",
|
|
1085
|
+
children: "Cancel"
|
|
1086
|
+
}
|
|
1087
|
+
),
|
|
1088
|
+
/* @__PURE__ */ jsx5(
|
|
1089
|
+
"button",
|
|
1090
|
+
{
|
|
1091
|
+
type: "button",
|
|
1092
|
+
disabled: !name.trim(),
|
|
1093
|
+
onClick: () => onSave({
|
|
1094
|
+
id: initial?.id,
|
|
1095
|
+
name: name.trim(),
|
|
1096
|
+
capabilities: caps,
|
|
1097
|
+
instructions: instructions.trim()
|
|
1098
|
+
}),
|
|
1099
|
+
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",
|
|
1100
|
+
children: "Save agent"
|
|
1101
|
+
}
|
|
1102
|
+
)
|
|
1103
|
+
] })
|
|
1746
1104
|
] });
|
|
1747
1105
|
}
|
|
1748
1106
|
|
|
1107
|
+
// src/chat/harness-model-compat.ts
|
|
1108
|
+
import { modelProvider, snapHarnessToModel } from "@tangle-network/agent-interface";
|
|
1109
|
+
|
|
1749
1110
|
// src/chat/artifact-agent-dock.tsx
|
|
1750
|
-
import * as
|
|
1111
|
+
import * as React6 from "react";
|
|
1751
1112
|
import { Sparkles as Sparkles2, Send, Square as Square2, X as X2 } from "lucide-react";
|
|
1752
1113
|
import { ChatMessage } from "@tangle-network/ui/chat";
|
|
1753
|
-
import { jsx as
|
|
1114
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1754
1115
|
function defaultScopeLabel(scope) {
|
|
1755
1116
|
if (scope.label) return scope.label;
|
|
1756
1117
|
switch (scope.kind) {
|
|
@@ -1796,23 +1157,23 @@ function ArtifactAgentDock({
|
|
|
1796
1157
|
onError,
|
|
1797
1158
|
className
|
|
1798
1159
|
}) {
|
|
1799
|
-
const [threadId, setThreadId] =
|
|
1800
|
-
const [messages, setMessages] =
|
|
1801
|
-
const [composer, setComposer] =
|
|
1802
|
-
const [streamContent, setStreamContent] =
|
|
1803
|
-
const [sending, setSending] =
|
|
1804
|
-
const [loading, setLoading] =
|
|
1805
|
-
const abortRef =
|
|
1806
|
-
const scrollRef =
|
|
1807
|
-
const scopeTokenRef =
|
|
1808
|
-
const reportError =
|
|
1160
|
+
const [threadId, setThreadId] = React6.useState(null);
|
|
1161
|
+
const [messages, setMessages] = React6.useState([]);
|
|
1162
|
+
const [composer, setComposer] = React6.useState(defaultPrompt ?? "");
|
|
1163
|
+
const [streamContent, setStreamContent] = React6.useState("");
|
|
1164
|
+
const [sending, setSending] = React6.useState(false);
|
|
1165
|
+
const [loading, setLoading] = React6.useState(false);
|
|
1166
|
+
const abortRef = React6.useRef(null);
|
|
1167
|
+
const scrollRef = React6.useRef(null);
|
|
1168
|
+
const scopeTokenRef = React6.useRef(0);
|
|
1169
|
+
const reportError = React6.useCallback(
|
|
1809
1170
|
(message) => {
|
|
1810
1171
|
if (onError) onError(message);
|
|
1811
1172
|
else if (typeof console !== "undefined") console.error("[ArtifactAgentDock]", message);
|
|
1812
1173
|
},
|
|
1813
1174
|
[onError]
|
|
1814
1175
|
);
|
|
1815
|
-
|
|
1176
|
+
React6.useEffect(() => {
|
|
1816
1177
|
if (!open) return;
|
|
1817
1178
|
scopeTokenRef.current += 1;
|
|
1818
1179
|
abortRef.current?.abort();
|
|
@@ -1848,11 +1209,11 @@ function ArtifactAgentDock({
|
|
|
1848
1209
|
cancelled = true;
|
|
1849
1210
|
};
|
|
1850
1211
|
}, [open, scope.kind, scope.key, transport, reportError]);
|
|
1851
|
-
|
|
1212
|
+
React6.useEffect(() => {
|
|
1852
1213
|
if (!scrollRef.current) return;
|
|
1853
1214
|
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
1854
1215
|
}, [messages.length, streamContent]);
|
|
1855
|
-
const send =
|
|
1216
|
+
const send = React6.useCallback(async () => {
|
|
1856
1217
|
const text = composer.trim();
|
|
1857
1218
|
if (!text || !threadId || sending) return;
|
|
1858
1219
|
const token = scopeTokenRef.current;
|
|
@@ -1902,7 +1263,7 @@ function ArtifactAgentDock({
|
|
|
1902
1263
|
if (abortRef.current === controller) abortRef.current = null;
|
|
1903
1264
|
}
|
|
1904
1265
|
}, [composer, threadId, sending, transport, reportError]);
|
|
1905
|
-
const cancel =
|
|
1266
|
+
const cancel = React6.useCallback(() => {
|
|
1906
1267
|
if (threadId && transport.cancel) {
|
|
1907
1268
|
try {
|
|
1908
1269
|
void Promise.resolve(transport.cancel({ threadId })).catch(() => {
|
|
@@ -1916,7 +1277,7 @@ function ArtifactAgentDock({
|
|
|
1916
1277
|
}, [threadId, transport]);
|
|
1917
1278
|
if (!open) return null;
|
|
1918
1279
|
const heading = defaultScopeLabel(scope);
|
|
1919
|
-
return /* @__PURE__ */
|
|
1280
|
+
return /* @__PURE__ */ jsxs5(
|
|
1920
1281
|
"aside",
|
|
1921
1282
|
{
|
|
1922
1283
|
className: cn(
|
|
@@ -1925,38 +1286,38 @@ function ArtifactAgentDock({
|
|
|
1925
1286
|
),
|
|
1926
1287
|
"aria-label": `Agent chat about ${heading}`,
|
|
1927
1288
|
children: [
|
|
1928
|
-
/* @__PURE__ */
|
|
1929
|
-
/* @__PURE__ */
|
|
1930
|
-
/* @__PURE__ */
|
|
1931
|
-
/* @__PURE__ */
|
|
1932
|
-
/* @__PURE__ */
|
|
1933
|
-
/* @__PURE__ */
|
|
1289
|
+
/* @__PURE__ */ jsxs5("header", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-low px-4 py-3", children: [
|
|
1290
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1291
|
+
/* @__PURE__ */ jsx6(Sparkles2, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
1292
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
|
|
1293
|
+
/* @__PURE__ */ jsx6("div", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Discussing" }),
|
|
1294
|
+
/* @__PURE__ */ jsx6("div", { className: "truncate text-sm font-semibold text-foreground", children: heading })
|
|
1934
1295
|
] })
|
|
1935
1296
|
] }),
|
|
1936
|
-
/* @__PURE__ */
|
|
1297
|
+
/* @__PURE__ */ jsx6(
|
|
1937
1298
|
"button",
|
|
1938
1299
|
{
|
|
1939
1300
|
type: "button",
|
|
1940
1301
|
onClick: () => onOpenChange(false),
|
|
1941
1302
|
"aria-label": "Close artifact chat",
|
|
1942
1303
|
className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground",
|
|
1943
|
-
children: /* @__PURE__ */
|
|
1304
|
+
children: /* @__PURE__ */ jsx6(X2, { className: "h-4 w-4" })
|
|
1944
1305
|
}
|
|
1945
1306
|
)
|
|
1946
1307
|
] }),
|
|
1947
|
-
/* @__PURE__ */
|
|
1948
|
-
/* @__PURE__ */
|
|
1949
|
-
/* @__PURE__ */
|
|
1308
|
+
/* @__PURE__ */ jsx6("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-4", children: loading ? /* @__PURE__ */ jsx6("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "Loading conversation\u2026" }) : messages.length === 0 && !streamContent ? /* @__PURE__ */ jsxs5("div", { className: "px-3 py-12 text-center", children: [
|
|
1309
|
+
/* @__PURE__ */ jsx6(Sparkles2, { className: "mx-auto mb-3 h-5 w-5 text-muted-foreground/40" }),
|
|
1310
|
+
/* @__PURE__ */ jsxs5("p", { className: "text-sm text-foreground", children: [
|
|
1950
1311
|
"Ask the agent about this ",
|
|
1951
1312
|
scope.kind === "vault-file" ? "document" : scope.kind,
|
|
1952
1313
|
"."
|
|
1953
1314
|
] }),
|
|
1954
|
-
/* @__PURE__ */
|
|
1955
|
-
] }) : /* @__PURE__ */
|
|
1956
|
-
messages.map((m) => /* @__PURE__ */
|
|
1957
|
-
streamContent && /* @__PURE__ */
|
|
1315
|
+
/* @__PURE__ */ jsx6("p", { className: "mt-1 text-xs text-muted-foreground", children: "It can read the current contents and propose edits, rewrites, or follow-ups." })
|
|
1316
|
+
] }) : /* @__PURE__ */ jsxs5("div", { className: "space-y-3", children: [
|
|
1317
|
+
messages.map((m) => /* @__PURE__ */ jsx6(ChatMessage, { role: m.role, content: m.content, timestamp: m.createdAt }, m.id)),
|
|
1318
|
+
streamContent && /* @__PURE__ */ jsx6(ChatMessage, { role: "assistant", content: streamContent, isStreaming: true })
|
|
1958
1319
|
] }) }),
|
|
1959
|
-
/* @__PURE__ */
|
|
1320
|
+
/* @__PURE__ */ jsx6(
|
|
1960
1321
|
"form",
|
|
1961
1322
|
{
|
|
1962
1323
|
className: "border-t border-[var(--md3-outline-variant)] bg-surface-container-low p-3",
|
|
@@ -1964,8 +1325,8 @@ function ArtifactAgentDock({
|
|
|
1964
1325
|
e.preventDefault();
|
|
1965
1326
|
void send();
|
|
1966
1327
|
},
|
|
1967
|
-
children: /* @__PURE__ */
|
|
1968
|
-
/* @__PURE__ */
|
|
1328
|
+
children: /* @__PURE__ */ jsxs5("div", { className: "flex items-end gap-2", children: [
|
|
1329
|
+
/* @__PURE__ */ jsx6(
|
|
1969
1330
|
"textarea",
|
|
1970
1331
|
{
|
|
1971
1332
|
value: composer,
|
|
@@ -1982,23 +1343,23 @@ function ArtifactAgentDock({
|
|
|
1982
1343
|
className: "min-h-[44px] flex-1 resize-none rounded-[20px] border border-[var(--md3-outline-variant)] bg-surface-dim px-3.5 py-2 text-sm text-foreground placeholder:text-muted-foreground transition-colors focus:outline-none focus:border-[var(--border-accent)] disabled:opacity-50"
|
|
1983
1344
|
}
|
|
1984
1345
|
),
|
|
1985
|
-
sending ? /* @__PURE__ */
|
|
1346
|
+
sending ? /* @__PURE__ */ jsx6(
|
|
1986
1347
|
"button",
|
|
1987
1348
|
{
|
|
1988
1349
|
type: "button",
|
|
1989
1350
|
onClick: cancel,
|
|
1990
1351
|
"aria-label": "Cancel",
|
|
1991
1352
|
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",
|
|
1992
|
-
children: /* @__PURE__ */
|
|
1353
|
+
children: /* @__PURE__ */ jsx6(Square2, { className: "h-4 w-4" })
|
|
1993
1354
|
}
|
|
1994
|
-
) : /* @__PURE__ */
|
|
1355
|
+
) : /* @__PURE__ */ jsx6(
|
|
1995
1356
|
"button",
|
|
1996
1357
|
{
|
|
1997
1358
|
type: "submit",
|
|
1998
1359
|
disabled: !composer.trim() || !threadId,
|
|
1999
1360
|
"aria-label": "Send",
|
|
2000
1361
|
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",
|
|
2001
|
-
children: /* @__PURE__ */
|
|
1362
|
+
children: /* @__PURE__ */ jsx6(Send, { className: "h-4 w-4" })
|
|
2002
1363
|
}
|
|
2003
1364
|
)
|
|
2004
1365
|
] })
|
|
@@ -2088,15 +1449,12 @@ export {
|
|
|
2088
1449
|
DEFAULT_REASONING_LEVEL_OPTIONS,
|
|
2089
1450
|
HARNESS_REASONING_OPTIONS,
|
|
2090
1451
|
ReasoningLevelPicker,
|
|
2091
|
-
isModelCompatibleWithHarness,
|
|
2092
|
-
snapModelToHarness,
|
|
2093
|
-
modelProvider,
|
|
2094
|
-
snapHarnessToModel,
|
|
2095
|
-
AgentProfilePicker,
|
|
2096
|
-
AgentSessionControls,
|
|
2097
1452
|
isAcceptedType,
|
|
2098
1453
|
validateComposerFiles,
|
|
2099
1454
|
AgentComposer,
|
|
1455
|
+
AgentProfilePicker,
|
|
1456
|
+
modelProvider,
|
|
1457
|
+
snapHarnessToModel,
|
|
2100
1458
|
ArtifactAgentDock,
|
|
2101
1459
|
createFetchTransport,
|
|
2102
1460
|
AgentTimeline,
|