@pgege/kaboo-react 0.1.0 → 0.2.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/index.js CHANGED
@@ -9,21 +9,33 @@ import {
9
9
  KabooInterruptHandler,
10
10
  MarkdownContent,
11
11
  MiniTable,
12
+ REFERENCES_STATE_KEY,
13
+ REFERENCE_METADATA_KEYS,
14
+ ReferenceStateSync,
15
+ ReferencesProvider,
12
16
  StructuredRenderersContext,
13
17
  Timeline,
14
18
  ToolRow,
19
+ attachmentToInputContent,
20
+ buildUserContent,
15
21
  directChildren,
16
22
  formatToolInput,
17
23
  formatToolResult,
24
+ mintReferenceId,
18
25
  normalizeResult,
26
+ objectToStateEntry,
19
27
  partitionChildrenByToolCall,
20
28
  pendingToolAnchorExists,
29
+ referenceMarker,
30
+ serializeReferences,
21
31
  topLevelGroups,
22
32
  useActivity,
23
33
  useDrill,
24
34
  useInterruptBridge,
25
- useInterruptFor
26
- } from "./chunk-RXZWOBSI.js";
35
+ useInterruptFor,
36
+ useReferences,
37
+ withReferenceState
38
+ } from "./chunk-4TDUEMK5.js";
27
39
 
28
40
  // src/context/KabooProvider.tsx
29
41
  import { CopilotKit } from "@copilotkit/react-core/v2";
@@ -36,6 +48,7 @@ function KabooProvider({
36
48
  interruptRenderers,
37
49
  disableInterruptHandler = false,
38
50
  disableInlineCards = false,
51
+ references,
39
52
  copilotKitProps,
40
53
  children
41
54
  }) {
@@ -47,17 +60,907 @@ function KabooProvider({
47
60
  threadId,
48
61
  useSingleEndpoint: false,
49
62
  ...copilotKitProps,
50
- children: /* @__PURE__ */ jsx(KabooActivityProvider, { agentId: agent, structuredRenderers, children: /* @__PURE__ */ jsx(DrillProvider, { children: /* @__PURE__ */ jsxs(InterruptBridgeProvider, { children: [
63
+ children: /* @__PURE__ */ jsx(KabooActivityProvider, { agentId: agent, structuredRenderers, children: /* @__PURE__ */ jsx(DrillProvider, { children: /* @__PURE__ */ jsx(InterruptBridgeProvider, { children: /* @__PURE__ */ jsxs(ReferencesProvider, { providers: references, syncObjectStateTo: agent, children: [
51
64
  !disableInterruptHandler && /* @__PURE__ */ jsx(KabooInterruptHandler, { agentId: agent, renderers: interruptRenderers }),
52
65
  !disableInlineCards && /* @__PURE__ */ jsx(KabooInlineCards, {}),
53
66
  children
54
- ] }) }) })
67
+ ] }) }) }) })
55
68
  }
56
69
  );
57
70
  }
58
71
 
72
+ // src/references/ReferenceInput.tsx
73
+ import {
74
+ forwardRef,
75
+ useCallback,
76
+ useEffect,
77
+ useLayoutEffect,
78
+ useMemo,
79
+ useRef,
80
+ useState
81
+ } from "react";
82
+ import { createPortal } from "react-dom";
83
+ import { CopilotChatInput, useAgent, useCopilotChatConfiguration } from "@copilotkit/react-core/v2";
84
+
85
+ // src/references/uploadProvider.tsx
86
+ var UPLOAD_MARKER = "__kabooUpload";
87
+ var DEFAULT_MAX_SIZE = 20 * 1024 * 1024;
88
+ function readAsBase64(file) {
89
+ return new Promise((resolve, reject) => {
90
+ const reader = new FileReader();
91
+ reader.onload = () => {
92
+ const result = String(reader.result);
93
+ const comma = result.indexOf(",");
94
+ resolve(comma >= 0 ? result.slice(comma + 1) : result);
95
+ };
96
+ reader.onerror = () => reject(reader.error);
97
+ reader.readAsDataURL(file);
98
+ });
99
+ }
100
+ async function uploadFileToReference(file, config) {
101
+ const kind = config.kind ?? "file";
102
+ const id = mintReferenceId(kind);
103
+ const maxSize = config.maxSize ?? DEFAULT_MAX_SIZE;
104
+ if (file.size > maxSize) {
105
+ throw new Error(`File "${file.name}" exceeds the ${maxSize}-byte limit.`);
106
+ }
107
+ if (config.onUpload) {
108
+ const result = await config.onUpload(file);
109
+ const mimeType = result.mimeType ?? file.type ?? "application/octet-stream";
110
+ const source = result.type === "url" ? { url: result.value } : { data: result.value };
111
+ return { transport: "attachment", kind, id, name: file.name, mimeType, source };
112
+ }
113
+ const data = await readAsBase64(file);
114
+ return {
115
+ transport: "attachment",
116
+ kind,
117
+ id,
118
+ name: file.name,
119
+ mimeType: file.type || "application/octet-stream",
120
+ source: { data }
121
+ };
122
+ }
123
+ function uploadProvider(config = {}) {
124
+ return {
125
+ id: config.id ?? "upload",
126
+ label: config.label ?? "Upload",
127
+ [UPLOAD_MARKER]: {
128
+ kind: config.kind ?? "file",
129
+ maxSize: config.maxSize ?? DEFAULT_MAX_SIZE,
130
+ accept: config.accept,
131
+ onUpload: config.onUpload
132
+ }
133
+ };
134
+ }
135
+ function isUploadProvider(provider) {
136
+ return UPLOAD_MARKER in provider;
137
+ }
138
+ function buildAttachmentsConfig(config = {}) {
139
+ const kind = config.kind ?? "file";
140
+ return {
141
+ enabled: true,
142
+ accept: config.accept,
143
+ maxSize: config.maxSize,
144
+ onUpload: async (file) => {
145
+ const id = mintReferenceId(kind);
146
+ const kabooMeta = {
147
+ [REFERENCE_METADATA_KEYS.id]: id,
148
+ [REFERENCE_METADATA_KEYS.kind]: kind,
149
+ [REFERENCE_METADATA_KEYS.name]: file.name
150
+ };
151
+ if (config.onUpload) {
152
+ const result = await config.onUpload(file);
153
+ return { ...result, metadata: { ...result.metadata ?? {}, ...kabooMeta } };
154
+ }
155
+ const value = await readAsBase64(file);
156
+ return {
157
+ type: "data",
158
+ value,
159
+ mimeType: file.type || "application/octet-stream",
160
+ metadata: kabooMeta
161
+ };
162
+ }
163
+ };
164
+ }
165
+
166
+ // src/references/ReferenceInput.tsx
167
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
168
+ var CHIP_CLASS = "kaboo-chip";
169
+ var NBSP = "\xA0";
170
+ var AT_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"/></svg>';
171
+ var FILE_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>';
172
+ var X_SVG = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>';
173
+ var AttachIcon = /* @__PURE__ */ jsx2("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
174
+ var AtIcon = /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
175
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "4" }),
176
+ /* @__PURE__ */ jsx2("path", { d: "M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94" })
177
+ ] });
178
+ var PlusIcon = /* @__PURE__ */ jsx2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "M5 12h14M12 5v14" }) });
179
+ var SearchIcon = /* @__PURE__ */ jsxs2("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
180
+ /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "11", r: "7" }),
181
+ /* @__PURE__ */ jsx2("path", { d: "m21 21-4.3-4.3" })
182
+ ] });
183
+ var POPOVER_WIDTH = 320;
184
+ var VIEWPORT_MARGIN = 8;
185
+ var ANCHOR_GAP = 6;
186
+ function computePosition(rect) {
187
+ if (!rect) return null;
188
+ const width = Math.min(POPOVER_WIDTH, window.innerWidth - VIEWPORT_MARGIN * 2);
189
+ const left = Math.max(
190
+ VIEWPORT_MARGIN,
191
+ Math.min(rect.left, window.innerWidth - width - VIEWPORT_MARGIN)
192
+ );
193
+ return { left, bottom: window.innerHeight - rect.top + ANCHOR_GAP, width };
194
+ }
195
+ function isImageMime(mime) {
196
+ return mime.startsWith("image/");
197
+ }
198
+ function previewSrc(ref) {
199
+ return "url" in ref.source ? ref.source.url : `data:${ref.mimeType};base64,${ref.source.data}`;
200
+ }
201
+ function serializeEditor(root) {
202
+ let out = "";
203
+ const walk = (node) => {
204
+ if (node.nodeType === Node.TEXT_NODE) {
205
+ out += node.textContent ?? "";
206
+ return;
207
+ }
208
+ if (node instanceof HTMLElement) {
209
+ if (node.classList.contains(CHIP_CLASS)) {
210
+ const name = node.dataset.name ?? "";
211
+ out += node.dataset.transport === "object" ? `@${name}` : name;
212
+ return;
213
+ }
214
+ if (node.tagName === "BR") {
215
+ out += "\n";
216
+ return;
217
+ }
218
+ if (node.tagName === "DIV" && out.length > 0 && !out.endsWith("\n")) out += "\n";
219
+ node.childNodes.forEach(walk);
220
+ }
221
+ };
222
+ root.childNodes.forEach(walk);
223
+ return out.replace(new RegExp(NBSP, "g"), " ");
224
+ }
225
+ function KabooReferenceInput({ trigger = "@", ...inputProps }) {
226
+ const {
227
+ providers,
228
+ pending,
229
+ addReference,
230
+ removeReference,
231
+ clearReferences,
232
+ recordMessageReferences
233
+ } = useReferences();
234
+ const config = useCopilotChatConfiguration();
235
+ const configAgentId = config?.agentId;
236
+ const { agent } = useAgent(configAgentId ? { agentId: configAgentId } : void 0);
237
+ const placeholder = config?.labels?.chatInputPlaceholder ?? "Type a message...";
238
+ const wrapRef = useRef(null);
239
+ const trayRef = useRef(null);
240
+ const menuRef = useRef(null);
241
+ const editorRef = useRef(null);
242
+ const fileInputRef = useRef(null);
243
+ const addBtnRef = useRef(null);
244
+ const searchRef = useRef(null);
245
+ const nativeChangeRef = useRef(void 0);
246
+ const anchorRef = useRef(() => null);
247
+ const tokenRef = useRef(null);
248
+ const replacingRef = useRef(null);
249
+ const replacingTrayIdRef = useRef(null);
250
+ const inlineIdsRef = useRef(/* @__PURE__ */ new Set());
251
+ const commitTargetRef = useRef("inline");
252
+ const [open, setOpen] = useState(false);
253
+ const [rows, setRows] = useState([]);
254
+ const [active, setActive] = useState(0);
255
+ const [query, setQuery] = useState("");
256
+ const [mode, setMode] = useState("token");
257
+ const [pos, setPos] = useState(null);
258
+ const placeholderRef = useRef(placeholder);
259
+ placeholderRef.current = placeholder;
260
+ const markEmpty = useCallback(() => {
261
+ const root = editorRef.current;
262
+ if (!root) return;
263
+ const isEmpty = root.querySelector(`.${CHIP_CLASS}`) === null && (root.textContent ?? "").trim().length === 0;
264
+ root.dataset.empty = isEmpty ? "true" : "false";
265
+ }, []);
266
+ const uploadProviderDef = useMemo(() => providers.find(isUploadProvider), [providers]);
267
+ const searchable = useMemo(() => providers.filter((p) => typeof p.search === "function"), [providers]);
268
+ const actionProviders = useMemo(
269
+ () => providers.filter((p) => typeof p.search !== "function"),
270
+ [providers]
271
+ );
272
+ const buildRows = useCallback(
273
+ async (q) => {
274
+ const next = [];
275
+ for (const provider of actionProviders) {
276
+ const label = isUploadProvider(provider) ? "Attach" : provider.label;
277
+ if (!q || label.toLowerCase().includes(q.toLowerCase())) {
278
+ next.push({ kind: "action", group: label, provider });
279
+ }
280
+ }
281
+ for (const provider of searchable) {
282
+ try {
283
+ const items = await provider.search(q);
284
+ for (const item of items) next.push({ kind: "item", group: provider.label, provider, item });
285
+ } catch {
286
+ }
287
+ }
288
+ return next;
289
+ },
290
+ [actionProviders, searchable]
291
+ );
292
+ const refresh = useCallback(
293
+ async (q) => {
294
+ const next = await buildRows(q);
295
+ setRows(next);
296
+ setActive(0);
297
+ },
298
+ [buildRows]
299
+ );
300
+ const close = useCallback(() => {
301
+ setOpen(false);
302
+ setRows([]);
303
+ setQuery("");
304
+ tokenRef.current = null;
305
+ replacingRef.current = null;
306
+ replacingTrayIdRef.current = null;
307
+ }, []);
308
+ const emit = useCallback(() => {
309
+ const root = editorRef.current;
310
+ if (!root) return;
311
+ const text = serializeEditor(root);
312
+ markEmpty();
313
+ nativeChangeRef.current?.({ target: { value: text } });
314
+ }, [markEmpty]);
315
+ const reconcile = useCallback(() => {
316
+ const root = editorRef.current;
317
+ if (!root) return;
318
+ const live = new Set(
319
+ Array.from(root.querySelectorAll(`.${CHIP_CLASS}`)).map((c) => c.dataset.refId)
320
+ );
321
+ for (const id of Array.from(inlineIdsRef.current)) {
322
+ if (!live.has(id)) {
323
+ inlineIdsRef.current.delete(id);
324
+ removeReference(id);
325
+ }
326
+ }
327
+ }, [removeReference]);
328
+ const focusEditor = useCallback(() => editorRef.current?.focus(), []);
329
+ const createChip = useCallback((ref) => {
330
+ const chip = document.createElement("span");
331
+ chip.className = `${CHIP_CLASS} ${CHIP_CLASS}-${ref.transport}`;
332
+ chip.contentEditable = "false";
333
+ chip.dataset.refId = ref.id;
334
+ chip.dataset.kind = ref.kind;
335
+ chip.dataset.name = ref.name;
336
+ chip.dataset.transport = ref.transport;
337
+ chip.title = ref.name;
338
+ if (ref.transport === "attachment" && isImageMime(ref.mimeType)) {
339
+ const img = document.createElement("img");
340
+ img.className = "kaboo-chip-thumb";
341
+ img.src = previewSrc(ref);
342
+ img.alt = ref.name;
343
+ chip.appendChild(img);
344
+ } else {
345
+ const ic = document.createElement("span");
346
+ ic.className = "kaboo-chip-ic";
347
+ ic.innerHTML = ref.transport === "object" ? AT_SVG : FILE_SVG;
348
+ chip.appendChild(ic);
349
+ }
350
+ const label = document.createElement("span");
351
+ label.className = "kaboo-chip-label";
352
+ label.textContent = ref.name;
353
+ chip.appendChild(label);
354
+ const x = document.createElement("button");
355
+ x.type = "button";
356
+ x.className = "kaboo-chip-x";
357
+ x.setAttribute("aria-label", `Remove ${ref.name}`);
358
+ x.innerHTML = X_SVG;
359
+ x.addEventListener("mousedown", (e) => {
360
+ e.preventDefault();
361
+ e.stopPropagation();
362
+ handlersRef.current.removeChip(chip);
363
+ });
364
+ chip.appendChild(x);
365
+ chip.addEventListener("mousedown", (e) => {
366
+ if (e.target === x || x.contains(e.target)) return;
367
+ e.preventDefault();
368
+ handlersRef.current.replaceChip(chip);
369
+ });
370
+ return chip;
371
+ }, []);
372
+ const insertAtCaret = useCallback((node) => {
373
+ const root = editorRef.current;
374
+ if (!root) return;
375
+ const sel = window.getSelection();
376
+ let range;
377
+ if (sel && sel.rangeCount > 0 && root.contains(sel.focusNode)) {
378
+ range = sel.getRangeAt(0);
379
+ } else {
380
+ range = document.createRange();
381
+ range.selectNodeContents(root);
382
+ range.collapse(false);
383
+ }
384
+ range.collapse(false);
385
+ const space = document.createTextNode(NBSP);
386
+ range.insertNode(space);
387
+ range.insertNode(node);
388
+ const after = document.createRange();
389
+ after.setStartAfter(space);
390
+ after.collapse(true);
391
+ sel?.removeAllRanges();
392
+ sel?.addRange(after);
393
+ }, []);
394
+ const placeChip = useCallback(
395
+ (ref) => {
396
+ const chip = createChip(ref);
397
+ const replacing = replacingRef.current;
398
+ if (replacing) {
399
+ const oldId = replacing.dataset.refId;
400
+ if (oldId && oldId !== ref.id) removeReference(oldId);
401
+ replacing.replaceWith(chip);
402
+ replacingRef.current = null;
403
+ } else if (tokenRef.current) {
404
+ const { node, start } = tokenRef.current;
405
+ const sel = window.getSelection();
406
+ const range = document.createRange();
407
+ range.setStart(node, Math.min(start, node.textContent?.length ?? 0));
408
+ if (sel && sel.focusNode && editorRef.current?.contains(sel.focusNode)) {
409
+ range.setEnd(sel.focusNode, sel.focusOffset);
410
+ } else {
411
+ range.setEnd(node, node.textContent?.length ?? 0);
412
+ }
413
+ range.deleteContents();
414
+ const space = document.createTextNode(NBSP);
415
+ range.insertNode(space);
416
+ range.insertNode(chip);
417
+ const after = document.createRange();
418
+ after.setStartAfter(space);
419
+ after.collapse(true);
420
+ sel?.removeAllRanges();
421
+ sel?.addRange(after);
422
+ tokenRef.current = null;
423
+ } else {
424
+ insertAtCaret(chip);
425
+ }
426
+ emit();
427
+ },
428
+ [createChip, insertAtCaret, removeReference, emit]
429
+ );
430
+ const commit = useCallback(
431
+ (ref, target) => {
432
+ addReference(ref);
433
+ if (target === "inline") {
434
+ inlineIdsRef.current.add(ref.id);
435
+ placeChip(ref);
436
+ } else {
437
+ const oldId = replacingTrayIdRef.current;
438
+ if (oldId && oldId !== ref.id) removeReference(oldId);
439
+ replacingTrayIdRef.current = null;
440
+ inlineIdsRef.current.delete(ref.id);
441
+ }
442
+ },
443
+ [addReference, placeChip, removeReference]
444
+ );
445
+ const openFilePicker = useCallback(
446
+ (provider, target) => {
447
+ const input = fileInputRef.current;
448
+ if (!input) return;
449
+ const cfg = isUploadProvider(provider) ? provider[UPLOAD_MARKER] : {};
450
+ input.accept = cfg.accept ?? "";
451
+ input.onchange = async () => {
452
+ const file = input.files?.[0];
453
+ input.value = "";
454
+ if (!file) return;
455
+ try {
456
+ const ref = await uploadFileToReference(file, cfg);
457
+ commit(ref, target);
458
+ } catch (err) {
459
+ console.error("[kaboo] upload failed", err);
460
+ }
461
+ };
462
+ input.click();
463
+ },
464
+ [commit]
465
+ );
466
+ const stripToken = useCallback(() => {
467
+ const t = tokenRef.current;
468
+ if (!t) return;
469
+ const sel = window.getSelection();
470
+ const range = document.createRange();
471
+ range.setStart(t.node, Math.min(t.start, t.node.textContent?.length ?? 0));
472
+ if (sel?.focusNode && editorRef.current?.contains(sel.focusNode)) {
473
+ range.setEnd(sel.focusNode, sel.focusOffset);
474
+ } else {
475
+ range.setEnd(t.node, t.node.textContent?.length ?? 0);
476
+ }
477
+ range.deleteContents();
478
+ emit();
479
+ }, [emit]);
480
+ const select = useCallback(
481
+ async (row) => {
482
+ const target = commitTargetRef.current;
483
+ if (row.kind === "action") {
484
+ const provider = row.provider;
485
+ if (target === "inline") stripToken();
486
+ close();
487
+ if (isUploadProvider(provider)) openFilePicker(provider, target);
488
+ else await provider.onSelect?.({ id: provider.id, label: provider.label });
489
+ focusEditor();
490
+ return;
491
+ }
492
+ await row.provider.onSelect?.(row.item);
493
+ const ref = row.provider.toReference?.(row.item);
494
+ if (ref) commit(ref, target);
495
+ close();
496
+ focusEditor();
497
+ },
498
+ [stripToken, close, openFilePicker, focusEditor, commit]
499
+ );
500
+ const openFromToken = useCallback(
501
+ (q) => {
502
+ commitTargetRef.current = "inline";
503
+ replacingRef.current = null;
504
+ replacingTrayIdRef.current = null;
505
+ anchorRef.current = () => {
506
+ const t = tokenRef.current;
507
+ return t ? caretRectFor(t.node, t.start) : null;
508
+ };
509
+ setMode("token");
510
+ setQuery(q);
511
+ setPos(computePosition(anchorRef.current()));
512
+ setOpen(true);
513
+ void refresh(q);
514
+ },
515
+ [refresh]
516
+ );
517
+ const toggleFromButton = useCallback(() => {
518
+ setOpen((prev) => {
519
+ if (prev) {
520
+ close();
521
+ return false;
522
+ }
523
+ commitTargetRef.current = "tray";
524
+ tokenRef.current = null;
525
+ replacingRef.current = null;
526
+ replacingTrayIdRef.current = null;
527
+ anchorRef.current = () => addBtnRef.current?.getBoundingClientRect() ?? null;
528
+ setMode("menu");
529
+ setQuery("");
530
+ setPos(computePosition(anchorRef.current()));
531
+ void refresh("");
532
+ return true;
533
+ });
534
+ }, [refresh, close]);
535
+ const onSearch = useCallback(
536
+ (value) => {
537
+ setQuery(value);
538
+ void refresh(value);
539
+ },
540
+ [refresh]
541
+ );
542
+ const handleNavKey = useCallback(
543
+ (e) => {
544
+ if (!open || rows.length === 0) return false;
545
+ if (e.key === "ArrowDown") {
546
+ e.preventDefault();
547
+ setActive((a) => (a + 1) % rows.length);
548
+ return true;
549
+ }
550
+ if (e.key === "ArrowUp") {
551
+ e.preventDefault();
552
+ setActive((a) => (a - 1 + rows.length) % rows.length);
553
+ return true;
554
+ }
555
+ if (e.key === "Enter" || e.key === "Tab") {
556
+ e.preventDefault();
557
+ void select(rows[active]);
558
+ return true;
559
+ }
560
+ if (e.key === "Escape") {
561
+ e.preventDefault();
562
+ close();
563
+ focusEditor();
564
+ return true;
565
+ }
566
+ return false;
567
+ },
568
+ [open, rows, active, select, close, focusEditor]
569
+ );
570
+ const replaceChip = useCallback(
571
+ (chip) => {
572
+ commitTargetRef.current = "inline";
573
+ replacingRef.current = chip;
574
+ replacingTrayIdRef.current = null;
575
+ tokenRef.current = null;
576
+ anchorRef.current = () => chip.getBoundingClientRect();
577
+ setMode("menu");
578
+ setQuery("");
579
+ setPos(computePosition(chip.getBoundingClientRect()));
580
+ setOpen(true);
581
+ void refresh("");
582
+ },
583
+ [refresh]
584
+ );
585
+ const replaceTrayChip = useCallback(
586
+ (id, anchorEl) => {
587
+ commitTargetRef.current = "tray";
588
+ replacingTrayIdRef.current = id;
589
+ replacingRef.current = null;
590
+ tokenRef.current = null;
591
+ anchorRef.current = () => anchorEl.getBoundingClientRect();
592
+ setMode("menu");
593
+ setQuery("");
594
+ setPos(computePosition(anchorEl.getBoundingClientRect()));
595
+ setOpen(true);
596
+ void refresh("");
597
+ },
598
+ [refresh]
599
+ );
600
+ const removeChip = useCallback(
601
+ (chip) => {
602
+ const id = chip.dataset.refId;
603
+ const next = chip.nextSibling;
604
+ chip.remove();
605
+ if (next && next.nodeType === Node.TEXT_NODE && next.textContent === NBSP) next.remove();
606
+ if (id) {
607
+ inlineIdsRef.current.delete(id);
608
+ removeReference(id);
609
+ }
610
+ emit();
611
+ focusEditor();
612
+ },
613
+ [removeReference, emit, focusEditor]
614
+ );
615
+ const handlersRef = useRef({ removeChip, replaceChip });
616
+ handlersRef.current = { removeChip, replaceChip };
617
+ const ctl = { open, rows, active, trigger, openFromToken, toggleFromButton, close, handleNavKey };
618
+ const ctlRef = useRef(ctl);
619
+ ctlRef.current = ctl;
620
+ const readToken = useCallback(() => {
621
+ const sel = window.getSelection();
622
+ if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return null;
623
+ const node = sel.focusNode;
624
+ if (!node || node.nodeType !== Node.TEXT_NODE) return null;
625
+ const text = node.textContent ?? "";
626
+ const before = text.slice(0, sel.focusOffset);
627
+ const at = before.lastIndexOf(ctlRef.current.trigger);
628
+ if (at < 0) return null;
629
+ const prev = at > 0 ? before[at - 1] : "";
630
+ if (prev && !/\s/.test(prev)) return null;
631
+ const q = before.slice(at + 1);
632
+ if (/\s/.test(q)) return null;
633
+ return { node, start: at, query: q };
634
+ }, []);
635
+ const onEditorInput = useCallback(() => {
636
+ reconcile();
637
+ emit();
638
+ const token = readToken();
639
+ if (token) {
640
+ tokenRef.current = { node: token.node, start: token.start };
641
+ const c = ctlRef.current;
642
+ if (!c.open || mode === "token") openFromToken(token.query);
643
+ else {
644
+ setQuery(token.query);
645
+ void refresh(token.query);
646
+ }
647
+ } else if (ctlRef.current.open && mode === "token") {
648
+ close();
649
+ }
650
+ }, [reconcile, emit, readToken, openFromToken, refresh, mode, close]);
651
+ const TextAreaSlot = useMemo(
652
+ () => forwardRef(function KabooRefEditor(props, ref) {
653
+ const { onChange, onKeyDown, className, autoFocus } = props;
654
+ nativeChangeRef.current = onChange;
655
+ const setRefs = (node) => {
656
+ editorRef.current = node;
657
+ if (typeof ref === "function") ref(node);
658
+ else if (ref) ref.current = node;
659
+ };
660
+ const handleKeyDown = (e) => {
661
+ if (ctlRef.current.handleNavKey(e)) return;
662
+ onKeyDown?.(e);
663
+ };
664
+ return /* @__PURE__ */ jsx2(
665
+ "div",
666
+ {
667
+ ref: setRefs,
668
+ role: "textbox",
669
+ "aria-multiline": "true",
670
+ tabIndex: 0,
671
+ contentEditable: true,
672
+ suppressContentEditableWarning: true,
673
+ "data-placeholder": placeholderRef.current,
674
+ className: `kaboo-ref-editor cpk:bg-transparent cpk:outline-none cpk:antialiased cpk:leading-relaxed cpk:text-[16px] ${className ?? ""}`,
675
+ "data-empty": "true",
676
+ autoFocus,
677
+ onInput: onEditorInput,
678
+ onKeyDown: handleKeyDown
679
+ }
680
+ );
681
+ }),
682
+ // eslint-disable-next-line react-hooks/exhaustive-deps
683
+ []
684
+ );
685
+ const AddMenuButtonSlot = useMemo(
686
+ () => function KabooRefAddButton(props) {
687
+ return /* @__PURE__ */ jsx2(
688
+ "button",
689
+ {
690
+ ref: addBtnRef,
691
+ type: "button",
692
+ className: "kaboo-ref-add",
693
+ "aria-label": "Add attachment or reference",
694
+ "aria-haspopup": "listbox",
695
+ "aria-expanded": ctlRef.current.open,
696
+ disabled: props.disabled,
697
+ onMouseDown: (e) => e.preventDefault(),
698
+ onClick: () => ctlRef.current.toggleFromButton(),
699
+ children: PlusIcon
700
+ }
701
+ );
702
+ },
703
+ []
704
+ );
705
+ useEffect(() => {
706
+ if (!open) return;
707
+ const onDocMouseDown = (e) => {
708
+ const target = e.target;
709
+ if (wrapRef.current?.contains(target) || menuRef.current?.contains(target)) return;
710
+ close();
711
+ };
712
+ const onDocKey = (e) => {
713
+ if (e.key === "Escape") close();
714
+ };
715
+ document.addEventListener("mousedown", onDocMouseDown);
716
+ document.addEventListener("keydown", onDocKey);
717
+ return () => {
718
+ document.removeEventListener("mousedown", onDocMouseDown);
719
+ document.removeEventListener("keydown", onDocKey);
720
+ };
721
+ }, [open, close]);
722
+ useEffect(() => {
723
+ if (!open) return;
724
+ const reposition = () => setPos(computePosition(anchorRef.current()));
725
+ window.addEventListener("resize", reposition);
726
+ window.addEventListener("scroll", reposition, true);
727
+ return () => {
728
+ window.removeEventListener("resize", reposition);
729
+ window.removeEventListener("scroll", reposition, true);
730
+ };
731
+ }, [open]);
732
+ useLayoutEffect(() => {
733
+ if (!open) return;
734
+ setPos(computePosition(anchorRef.current()));
735
+ if (mode === "menu") searchRef.current?.focus();
736
+ }, [open, mode, rows.length]);
737
+ useEffect(() => {
738
+ const target = agent;
739
+ if (!target || typeof target.subscribe !== "function") return;
740
+ const sub = target.subscribe({ onRunStartedEvent: () => clearReferences() });
741
+ return () => sub?.unsubscribe?.();
742
+ }, [agent, clearReferences]);
743
+ useEffect(() => {
744
+ const root = editorRef.current;
745
+ if (!root) return;
746
+ if ((inputProps.value ?? "") === "" && root.childNodes.length > 0) {
747
+ root.textContent = "";
748
+ markEmpty();
749
+ }
750
+ }, [inputProps.value, markEmpty]);
751
+ const handleSubmit = useCallback(
752
+ (message) => {
753
+ const text = message.trim();
754
+ const { attachmentParts } = serializeReferences(pending);
755
+ const target = agent;
756
+ if (text.length === 0 && attachmentParts.length === 0) return;
757
+ if (!target?.addMessage || !target.runAgent) {
758
+ inputProps.onSubmitMessage?.(message);
759
+ close();
760
+ return;
761
+ }
762
+ const content = attachmentParts.length > 0 ? buildUserContent(text, attachmentParts) : text;
763
+ const id = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2);
764
+ recordMessageReferences(
765
+ id,
766
+ pending.filter((r) => r.transport === "object")
767
+ );
768
+ target.addMessage({ id, role: "user", content });
769
+ void Promise.resolve(target.runAgent()).catch(
770
+ (err) => console.error("[kaboo] runAgent failed", err)
771
+ );
772
+ const root = editorRef.current;
773
+ if (root) root.textContent = "";
774
+ markEmpty();
775
+ close();
776
+ },
777
+ [pending, agent, inputProps, close, markEmpty, recordMessageReferences]
778
+ );
779
+ const trayRefs = pending.filter((r) => !inlineIdsRef.current.has(r.id));
780
+ useLayoutEffect(() => {
781
+ const tray = trayRef.current;
782
+ const wrap = wrapRef.current;
783
+ if (!tray || !wrap) return;
784
+ const align = () => {
785
+ const pill = wrap.querySelector(".copilotKitInput");
786
+ if (!pill) return;
787
+ const pr = pill.getBoundingClientRect();
788
+ const wr = wrap.getBoundingClientRect();
789
+ tray.style.marginLeft = `${Math.max(0, pr.left - wr.left)}px`;
790
+ tray.style.width = `${pr.width}px`;
791
+ };
792
+ align();
793
+ window.addEventListener("resize", align);
794
+ return () => window.removeEventListener("resize", align);
795
+ }, [trayRefs.length]);
796
+ return /* @__PURE__ */ jsxs2("div", { ref: wrapRef, className: "kaboo-ref-input", children: [
797
+ /* @__PURE__ */ jsx2("input", { ref: fileInputRef, type: "file", className: "kaboo-ref-file", tabIndex: -1, "aria-hidden": "true" }),
798
+ trayRefs.length > 0 && /* @__PURE__ */ jsx2("div", { ref: trayRef, className: "kaboo-ref-tray", children: trayRefs.map((r) => /* @__PURE__ */ jsx2(
799
+ TrayChip,
800
+ {
801
+ reference: r,
802
+ onRemove: () => removeReference(r.id),
803
+ onReplace: (el) => replaceTrayChip(r.id, el)
804
+ },
805
+ r.id
806
+ )) }),
807
+ /* @__PURE__ */ jsx2(
808
+ CopilotChatInput,
809
+ {
810
+ ...inputProps,
811
+ onSubmitMessage: handleSubmit,
812
+ textArea: TextAreaSlot,
813
+ addMenuButton: AddMenuButtonSlot
814
+ }
815
+ ),
816
+ open && pos && createPortal(
817
+ /* @__PURE__ */ jsx2(
818
+ ReferencePopover,
819
+ {
820
+ ref: menuRef,
821
+ pos,
822
+ rows,
823
+ active,
824
+ query,
825
+ mode,
826
+ searchRef,
827
+ hasUpload: !!uploadProviderDef,
828
+ onSearch,
829
+ onSearchKeyDown: handleNavKey,
830
+ onHover: setActive,
831
+ onPick: select
832
+ }
833
+ ),
834
+ document.body
835
+ )
836
+ ] });
837
+ }
838
+ function TrayChip({
839
+ reference,
840
+ onRemove,
841
+ onReplace
842
+ }) {
843
+ const ref = useRef(null);
844
+ const isImage = reference.transport === "attachment" && isImageMime(reference.mimeType);
845
+ return /* @__PURE__ */ jsxs2(
846
+ "span",
847
+ {
848
+ ref,
849
+ className: `kaboo-chip ${CHIP_CLASS}-${reference.transport} kaboo-chip-tray`,
850
+ title: reference.name,
851
+ role: "button",
852
+ tabIndex: 0,
853
+ onMouseDown: (e) => {
854
+ e.preventDefault();
855
+ if (ref.current) onReplace(ref.current);
856
+ },
857
+ children: [
858
+ isImage ? /* @__PURE__ */ jsx2(
859
+ "img",
860
+ {
861
+ className: "kaboo-chip-thumb",
862
+ src: previewSrc(reference),
863
+ alt: reference.name
864
+ }
865
+ ) : /* @__PURE__ */ jsx2("span", { className: "kaboo-chip-ic", children: reference.transport === "object" ? AtIcon : AttachIcon }),
866
+ /* @__PURE__ */ jsx2("span", { className: "kaboo-chip-label", children: reference.name }),
867
+ /* @__PURE__ */ jsx2(
868
+ "button",
869
+ {
870
+ type: "button",
871
+ className: "kaboo-chip-x",
872
+ "aria-label": `Remove ${reference.name}`,
873
+ onMouseDown: (e) => {
874
+ e.preventDefault();
875
+ e.stopPropagation();
876
+ onRemove();
877
+ },
878
+ children: /* @__PURE__ */ jsx2("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "M18 6 6 18M6 6l12 12" }) })
879
+ }
880
+ )
881
+ ]
882
+ }
883
+ );
884
+ }
885
+ function caretRectFor(node, offset) {
886
+ try {
887
+ const range = document.createRange();
888
+ range.setStart(node, Math.min(offset, node.textContent?.length ?? 0));
889
+ range.collapse(true);
890
+ const rect = range.getBoundingClientRect();
891
+ if (rect.top === 0 && rect.left === 0) {
892
+ const parent = node.parentElement;
893
+ return parent ? parent.getBoundingClientRect() : null;
894
+ }
895
+ return rect;
896
+ } catch {
897
+ return null;
898
+ }
899
+ }
900
+ var ReferencePopover = forwardRef(function ReferencePopover2({ pos, rows, active, query, mode, searchRef, onSearch, onSearchKeyDown, onHover, onPick }, ref) {
901
+ return /* @__PURE__ */ jsxs2(
902
+ "div",
903
+ {
904
+ ref,
905
+ className: "kaboo-refmenu",
906
+ role: "listbox",
907
+ style: { left: pos.left, bottom: pos.bottom, width: pos.width },
908
+ children: [
909
+ mode === "menu" && /* @__PURE__ */ jsxs2("div", { className: "kaboo-refmenu-search", children: [
910
+ /* @__PURE__ */ jsx2("span", { className: "kaboo-refmenu-search-icon", children: SearchIcon }),
911
+ /* @__PURE__ */ jsx2(
912
+ "input",
913
+ {
914
+ ref: searchRef,
915
+ type: "text",
916
+ className: "kaboo-refmenu-search-input",
917
+ placeholder: "Search\u2026",
918
+ value: query,
919
+ onChange: (e) => onSearch(e.target.value),
920
+ onKeyDown: (e) => onSearchKeyDown(e)
921
+ }
922
+ )
923
+ ] }),
924
+ rows.length === 0 && /* @__PURE__ */ jsx2("div", { className: "kaboo-refmenu-empty", children: "No matches" }),
925
+ rows.map((row, i) => {
926
+ const key = row.kind === "action" ? `action:${row.provider.id}` : `${row.provider.id}:${row.item.id}`;
927
+ const showHeader = i === 0 || rows[i - 1].group !== row.group;
928
+ const isUpload = row.kind === "action" && isUploadProvider(row.provider);
929
+ const icon = isUpload ? AttachIcon : row.kind === "item" ? row.provider.icon ?? AtIcon : row.provider.icon ?? AtIcon;
930
+ const title = row.kind === "action" ? isUpload ? "Attach a file" : row.provider.label : row.item.label;
931
+ const subtitle = row.kind === "action" ? isUpload ? "Upload from your device" : void 0 : row.item.description;
932
+ const custom = row.kind === "item" ? row.provider.renderItem?.(row.item) : void 0;
933
+ return /* @__PURE__ */ jsxs2("div", { children: [
934
+ showHeader && /* @__PURE__ */ jsx2("div", { className: "kaboo-refmenu-group", children: row.group }),
935
+ /* @__PURE__ */ jsxs2(
936
+ "div",
937
+ {
938
+ role: "option",
939
+ "aria-selected": i === active,
940
+ className: `kaboo-refmenu-item${i === active ? " is-active" : ""}`,
941
+ onMouseEnter: () => onHover(i),
942
+ onMouseDown: (e) => {
943
+ e.preventDefault();
944
+ onPick(row);
945
+ },
946
+ children: [
947
+ /* @__PURE__ */ jsx2("span", { className: "kaboo-refmenu-icon", children: icon }),
948
+ custom ?? /* @__PURE__ */ jsxs2("span", { className: "kaboo-refmenu-text", children: [
949
+ /* @__PURE__ */ jsx2("span", { className: "kaboo-refmenu-title", children: title }),
950
+ subtitle && /* @__PURE__ */ jsx2("span", { className: "kaboo-refmenu-sub", children: subtitle })
951
+ ] })
952
+ ]
953
+ }
954
+ )
955
+ ] }, key);
956
+ })
957
+ ]
958
+ }
959
+ );
960
+ });
961
+
59
962
  // src/components/ActivityPanel.tsx
60
- import { jsx as jsx2 } from "react/jsx-runtime";
963
+ import { jsx as jsx3 } from "react/jsx-runtime";
61
964
  function ActivityPanel() {
62
965
  const { groups } = useActivity();
63
966
  const { activeDrill } = useDrill();
@@ -66,14 +969,14 @@ function ActivityPanel() {
66
969
  if (filtered.length === 0 && activeDrill) {
67
970
  const exact = groups[activeDrill];
68
971
  if (exact) {
69
- return /* @__PURE__ */ jsx2("div", { className: "kaboo-activity-panel", children: /* @__PURE__ */ jsx2(AgentCard, { groupId: activeDrill, group: exact }) });
972
+ return /* @__PURE__ */ jsx3("div", { className: "kaboo-activity-panel", children: /* @__PURE__ */ jsx3(AgentCard, { groupId: activeDrill, group: exact }) });
70
973
  }
71
974
  }
72
- return /* @__PURE__ */ jsx2("div", { className: "kaboo-activity-panel", children: filtered.map(([id, group]) => /* @__PURE__ */ jsx2(AgentCard, { groupId: id, group }, id)) });
975
+ return /* @__PURE__ */ jsx3("div", { className: "kaboo-activity-panel", children: filtered.map(([id, group]) => /* @__PURE__ */ jsx3(AgentCard, { groupId: id, group }, id)) });
73
976
  }
74
977
 
75
978
  // src/components/GlassTabs.tsx
76
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
979
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
77
980
  function GlassTabs() {
78
981
  const { drillPath, drillToRoot, drillToLevel } = useDrill();
79
982
  const { groups } = useActivity();
@@ -86,11 +989,11 @@ function GlassTabs() {
86
989
  level: i
87
990
  }))
88
991
  ];
89
- return /* @__PURE__ */ jsx3("nav", { className: "kaboo-breadcrumb-bar", children: tabs.map((tab, i) => {
992
+ return /* @__PURE__ */ jsx4("nav", { className: "kaboo-breadcrumb-bar", children: tabs.map((tab, i) => {
90
993
  const isLast = i === tabs.length - 1;
91
- return /* @__PURE__ */ jsxs2("span", { className: "kaboo-breadcrumb-item", children: [
92
- i > 0 && /* @__PURE__ */ jsx3("svg", { width: 12, height: 12, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-sep", children: /* @__PURE__ */ jsx3("path", { d: "M6 4l4 4-4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }),
93
- /* @__PURE__ */ jsxs2(
994
+ return /* @__PURE__ */ jsxs3("span", { className: "kaboo-breadcrumb-item", children: [
995
+ i > 0 && /* @__PURE__ */ jsx4("svg", { width: 12, height: 12, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-sep", children: /* @__PURE__ */ jsx4("path", { d: "M6 4l4 4-4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }),
996
+ /* @__PURE__ */ jsxs3(
94
997
  "button",
95
998
  {
96
999
  className: `kaboo-breadcrumb-link ${isLast ? "kaboo-breadcrumb-current" : ""}`,
@@ -101,7 +1004,7 @@ function GlassTabs() {
101
1004
  },
102
1005
  disabled: isLast,
103
1006
  children: [
104
- i === 0 && /* @__PURE__ */ jsx3("svg", { width: 14, height: 14, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-icon", children: /* @__PURE__ */ jsx3("path", { d: "M3 8.5L8 4l5 4.5M4.5 7.5V12h2.5V9.5h2V12h2.5V7.5", fill: "none", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1007
+ i === 0 && /* @__PURE__ */ jsx4("svg", { width: 14, height: 14, viewBox: "0 0 16 16", className: "kaboo-breadcrumb-icon", children: /* @__PURE__ */ jsx4("path", { d: "M3 8.5L8 4l5 4.5M4.5 7.5V12h2.5V9.5h2V12h2.5V7.5", fill: "none", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
105
1008
  tab.label
106
1009
  ]
107
1010
  }
@@ -111,20 +1014,20 @@ function GlassTabs() {
111
1014
  }
112
1015
 
113
1016
  // src/components/DrillDetailView.tsx
114
- import { useEffect, useRef } from "react";
115
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1017
+ import { useEffect as useEffect2, useRef as useRef2 } from "react";
1018
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
116
1019
  function DrillDetailView() {
117
1020
  const { activeDrill } = useDrill();
118
1021
  const { groups } = useActivity();
119
1022
  const { active: activeInterrupts } = useInterruptBridge();
120
- const bodyRef = useRef(null);
121
- useEffect(() => {
1023
+ const bodyRef = useRef2(null);
1024
+ useEffect2(() => {
122
1025
  if (activeDrill && bodyRef.current) {
123
1026
  bodyRef.current.scrollTop = 0;
124
1027
  }
125
1028
  }, [activeDrill]);
126
1029
  if (!activeDrill) {
127
- return /* @__PURE__ */ jsx4("div", { className: "kaboo-drill-detail kaboo-hidden" });
1030
+ return /* @__PURE__ */ jsx5("div", { className: "kaboo-drill-detail kaboo-hidden" });
128
1031
  }
129
1032
  const group = groups[activeDrill];
130
1033
  const timeline = group?.timeline ?? [];
@@ -136,15 +1039,15 @@ function DrillDetailView() {
136
1039
  const renderToolCard = (toolUseId) => {
137
1040
  const entry = childByToolCall.get(toolUseId);
138
1041
  if (!entry) return null;
139
- return /* @__PURE__ */ jsx4(AgentCard, { groupId: entry[0], group: entry[1] });
1042
+ return /* @__PURE__ */ jsx5(AgentCard, { groupId: entry[0], group: entry[1] });
140
1043
  };
141
- return /* @__PURE__ */ jsx4("div", { ref: bodyRef, className: "kaboo-drill-detail", children: /* @__PURE__ */ jsxs3("div", { className: "kaboo-drill-detail-inner", children: [
142
- group?.task && /* @__PURE__ */ jsxs3("div", { className: "kaboo-agent-card-task kaboo-drill-task", children: [
143
- /* @__PURE__ */ jsx4("strong", { children: "Task:" }),
1044
+ return /* @__PURE__ */ jsx5("div", { ref: bodyRef, className: "kaboo-drill-detail", children: /* @__PURE__ */ jsxs4("div", { className: "kaboo-drill-detail-inner", children: [
1045
+ group?.task && /* @__PURE__ */ jsxs4("div", { className: "kaboo-agent-card-task kaboo-drill-task", children: [
1046
+ /* @__PURE__ */ jsx5("strong", { children: "Task:" }),
144
1047
  " ",
145
1048
  group.task
146
1049
  ] }),
147
- group && timeline.length > 0 && /* @__PURE__ */ jsx4("div", { className: "kaboo-drill-timeline", children: /* @__PURE__ */ jsx4(
1050
+ group && timeline.length > 0 && /* @__PURE__ */ jsx5("div", { className: "kaboo-drill-timeline", children: /* @__PURE__ */ jsx5(
148
1051
  Timeline,
149
1052
  {
150
1053
  timeline,
@@ -153,7 +1056,7 @@ function DrillDetailView() {
153
1056
  renderToolCard
154
1057
  }
155
1058
  ) }),
156
- unanchoredInterrupts.length > 0 && /* @__PURE__ */ jsx4("div", { className: "kaboo-drill-interrupt", children: unanchoredInterrupts.map((it) => /* @__PURE__ */ jsx4(
1059
+ unanchoredInterrupts.length > 0 && /* @__PURE__ */ jsx5("div", { className: "kaboo-drill-interrupt", children: unanchoredInterrupts.map((it) => /* @__PURE__ */ jsx5(
157
1060
  InterruptRenderer,
158
1061
  {
159
1062
  reason: it.reason,
@@ -163,12 +1066,12 @@ function DrillDetailView() {
163
1066
  },
164
1067
  it.id
165
1068
  )) }),
166
- leftoverChildren.length > 0 && /* @__PURE__ */ jsxs3("div", { className: "kaboo-drill-children", children: [
167
- /* @__PURE__ */ jsx4("div", { className: "kaboo-drill-section-label", children: "Sub-agents" }),
168
- leftoverChildren.map(([id, childGroup]) => /* @__PURE__ */ jsx4(AgentCard, { groupId: id, group: childGroup }, id))
1069
+ leftoverChildren.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "kaboo-drill-children", children: [
1070
+ /* @__PURE__ */ jsx5("div", { className: "kaboo-drill-section-label", children: "Sub-agents" }),
1071
+ leftoverChildren.map(([id, childGroup]) => /* @__PURE__ */ jsx5(AgentCard, { groupId: id, group: childGroup }, id))
169
1072
  ] }),
170
- group && timeline.length === 0 && childEntries.length === 0 && /* @__PURE__ */ jsx4("div", { className: "kaboo-agent-card-empty", children: "Working..." }),
171
- !group && /* @__PURE__ */ jsx4("div", { className: "kaboo-agent-card-empty", children: "No activity data for this group." })
1073
+ group && timeline.length === 0 && childEntries.length === 0 && /* @__PURE__ */ jsx5("div", { className: "kaboo-agent-card-empty", children: "Working..." }),
1074
+ !group && /* @__PURE__ */ jsx5("div", { className: "kaboo-agent-card-empty", children: "No activity data for this group." })
172
1075
  ] }) });
173
1076
  }
174
1077
  export {
@@ -182,19 +1085,37 @@ export {
182
1085
  InterruptRenderer,
183
1086
  KabooActivityProvider,
184
1087
  KabooProvider,
1088
+ KabooReferenceInput,
185
1089
  MarkdownContent,
186
1090
  MiniTable,
1091
+ REFERENCES_STATE_KEY,
1092
+ REFERENCE_METADATA_KEYS,
1093
+ ReferenceStateSync,
1094
+ ReferencesProvider,
187
1095
  StructuredRenderersContext,
188
1096
  Timeline,
189
1097
  ToolRow,
1098
+ UPLOAD_MARKER,
1099
+ attachmentToInputContent,
1100
+ buildAttachmentsConfig,
1101
+ buildUserContent,
190
1102
  directChildren,
191
1103
  formatToolInput,
192
1104
  formatToolResult,
1105
+ isUploadProvider,
1106
+ mintReferenceId,
193
1107
  normalizeResult,
1108
+ objectToStateEntry,
1109
+ referenceMarker,
1110
+ serializeReferences,
194
1111
  topLevelGroups,
1112
+ uploadFileToReference,
1113
+ uploadProvider,
195
1114
  useActivity,
196
1115
  useDrill,
197
1116
  useInterruptBridge,
198
- useInterruptFor
1117
+ useInterruptFor,
1118
+ useReferences,
1119
+ withReferenceState
199
1120
  };
200
1121
  //# sourceMappingURL=index.js.map