ghostykit 0.6.4 → 0.7.1

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.
@@ -1,6 +1,4 @@
1
1
  import {
2
- TOOL_VERBS_EN,
3
- TOOL_VERBS_ES,
4
2
  applyJsonPatch,
5
3
  argsDetail,
6
4
  beginSend,
@@ -9,10 +7,9 @@ import {
9
7
  createPilotState,
10
8
  dropLastExchange,
11
9
  expiryMs,
10
+ extractBlocks,
12
11
  extractReplyOptions,
13
- groupSteps,
14
12
  isNarration,
15
- isRunning,
16
13
  lastUserTurn,
17
14
  markSubagent,
18
15
  messagesToTurns,
@@ -23,20 +20,77 @@ import {
23
20
  resolvePermission,
24
21
  sendFailed,
25
22
  splitNarration,
26
- stepLabel,
27
23
  stopTurn,
28
24
  subagentsOf,
29
- summarizeTools,
30
25
  takeQueued,
31
- toPermission,
32
- toolName,
33
- toolNames
34
- } from "../chunks/chunk-LPPND5ME.js";
26
+ toPermission
27
+ } from "../chunks/chunk-7M2B7VKH.js";
35
28
  import {
36
29
  PilotHttpError,
37
30
  createAgUiTransport,
38
31
  readSse
39
- } from "../chunks/chunk-TMEHQZWL.js";
32
+ } from "../chunks/chunk-KG23PQI2.js";
33
+ import {
34
+ Activity,
35
+ AppWindow,
36
+ ArrowDown,
37
+ ArrowUpRight,
38
+ BUILTIN_CUSTOM,
39
+ BookOpen,
40
+ Check,
41
+ ChevronRight,
42
+ CircleCheck,
43
+ CircleSlash,
44
+ CircleX,
45
+ Clock,
46
+ Coins,
47
+ Composer,
48
+ Copy,
49
+ ExternalLink,
50
+ FilePenLine,
51
+ FileSpreadsheet,
52
+ FileText,
53
+ Folder,
54
+ Gauge,
55
+ Globe,
56
+ Image,
57
+ Inbox,
58
+ ListChecks,
59
+ LoaderCircle,
60
+ ModelPicker,
61
+ OctagonX,
62
+ PilotoContext,
63
+ ReferenceChips,
64
+ RotateCcw,
65
+ STRINGS_EN,
66
+ STRINGS_ES,
67
+ Search,
68
+ Send,
69
+ ShieldAlert,
70
+ Suggestions,
71
+ TurnContext,
72
+ UsersRound,
73
+ Wrench,
74
+ X,
75
+ definePlugin,
76
+ isDev,
77
+ mimeOf,
78
+ pluginPoints,
79
+ pluginRequires,
80
+ resolvePlugins,
81
+ rootProps,
82
+ usePiloto
83
+ } from "../chunks/chunk-PNU3LKWG.js";
84
+ import {
85
+ TOOL_VERBS_EN,
86
+ TOOL_VERBS_ES,
87
+ groupSteps,
88
+ isRunning,
89
+ stepLabel,
90
+ summarizeTools,
91
+ toolName,
92
+ toolNames
93
+ } from "../chunks/chunk-UUH37KXA.js";
40
94
  import {
41
95
  applyReferences,
42
96
  cleanUserText,
@@ -44,790 +98,16 @@ import {
44
98
  systemNoteOf
45
99
  } from "../chunks/chunk-6U545WJG.js";
46
100
 
47
- // ../../node_modules/lucide-react/dist/esm/createLucideIcon.mjs
48
- import { forwardRef as forwardRef2, createElement as createElement3 } from "react";
49
-
50
- // ../../node_modules/lucide-react/dist/esm/shared/src/utils/toKebabCase.mjs
51
- var toKebabCase = (string) => string?.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
52
-
53
- // ../../node_modules/lucide-react/dist/esm/shared/src/utils/toLucideIconData.mjs
54
- function toLucideIconData(iconName, iconNode, aliases = []) {
55
- if (iconNode == null) {
56
- throw new Error("[lucide]: iconNode is required when icon name is used");
57
- }
58
- return {
59
- name: toKebabCase(iconName),
60
- size: 24,
61
- node: iconNode,
62
- ...aliases.length > 0 ? { aliases } : {}
63
- };
64
- }
65
-
66
- // ../../node_modules/lucide-react/dist/esm/shared/src/utils/toCamelCase.mjs
67
- var toCamelCase = (string) => {
68
- let out = "";
69
- let upperNext = false;
70
- for (const ch of string) {
71
- if (ch === "-" || ch === "_" || ch <= " ") {
72
- upperNext = out.length > 0;
73
- continue;
74
- }
75
- if (out.length === 0) {
76
- out += ch.toLowerCase();
77
- } else {
78
- out += upperNext ? ch.toUpperCase() : ch;
79
- }
80
- upperNext = false;
81
- }
82
- return out;
83
- };
84
-
85
- // ../../node_modules/lucide-react/dist/esm/shared/src/utils/toPascalCase.mjs
86
- var toPascalCase = (string) => {
87
- const camelCase = toCamelCase(string);
88
- return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
89
- };
90
-
91
- // ../../node_modules/lucide-react/dist/esm/Icon.mjs
92
- import { forwardRef, createElement as createElement2 } from "react";
93
-
94
- // ../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.mjs
95
- var mergeClasses = (...classes) => classes.filter((className, index, array) => {
96
- return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
97
- }).join(" ").trim();
98
-
99
- // ../../node_modules/lucide-react/dist/esm/shared/src/build/defaultAttributes.mjs
100
- var defaultAttributes = {
101
- xmlns: "http://www.w3.org/2000/svg",
102
- width: 24,
103
- height: 24,
104
- viewBox: "0 0 24 24",
105
- fill: "none",
106
- stroke: "currentColor",
107
- "stroke-width": 2,
108
- "stroke-linecap": "round",
109
- "stroke-linejoin": "round"
110
- };
111
-
112
- // ../../node_modules/lucide-react/dist/esm/shared/src/build/buildLucideIconNode.mjs
113
- function isDefined(value) {
114
- return value !== null && value !== void 0;
115
- }
116
- function buildLucideIconNode(icon, params = {}) {
117
- const attributeNames = params.attributeNames ?? {};
118
- const getAttributeName = (attributeName) => attributeNames[attributeName] ?? attributeName;
119
- const viewBoxWidth = icon.size ?? icon.width ?? defaultAttributes["width"];
120
- const viewBoxHeight = icon.size ?? icon.height ?? defaultAttributes["height"];
121
- const aliasClassNames = icon.aliases?.filter((alias) => typeof alias === "string" && alias.trim() !== "").map((alias) => `lucide-${alias}`) ?? [];
122
- const iconClassNames = [...icon.name ? [`lucide-${icon.name}`] : [], ...aliasClassNames];
123
- const classNamesFromClassName = params.className?.split(" ").filter(Boolean) ?? [];
124
- const className = params.includeDefaultClasses === false ? mergeClasses(...classNamesFromClassName) : mergeClasses("lucide", ...iconClassNames, ...classNamesFromClassName);
125
- const calculatedStrokeWidth = params.absoluteStrokeWidth ? Number(params.strokeWidth ?? defaultAttributes["stroke-width"]) * Number(icon.size ?? icon.width ?? defaultAttributes["width"]) / Number(params.size ?? params.width ?? defaultAttributes["width"]) : params.strokeWidth ?? defaultAttributes["stroke-width"];
126
- const attributes = {
127
- ...Object.entries(defaultAttributes).reduce((attrs, [attrName, value]) => {
128
- attrs[getAttributeName(attrName)] = value;
129
- return attrs;
130
- }, {}),
131
- ..."color" in params && params.color && {
132
- [getAttributeName("stroke")]: params.color
133
- },
134
- ..."size" in params && isDefined(params.size) && {
135
- [getAttributeName("width")]: params.size,
136
- [getAttributeName("height")]: params.size
137
- },
138
- ..."width" in params && isDefined(params.width) && {
139
- [getAttributeName("width")]: params.width
140
- },
141
- ..."height" in params && isDefined(params.height) && {
142
- [getAttributeName("height")]: params.height
143
- },
144
- [getAttributeName("stroke-width")]: calculatedStrokeWidth,
145
- ...className && {
146
- [getAttributeName("class")]: className
147
- },
148
- [getAttributeName("viewBox")]: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
149
- ...params.hasA11yProp === false ? {
150
- [getAttributeName("aria-hidden")]: "true"
151
- } : {},
152
- ..."attributes" in params && params.attributes
153
- };
154
- return [
155
- "svg",
156
- attributes,
157
- icon.node.map((child) => {
158
- const [name, attrs, children] = child;
159
- const nextAttrs = params.nonScalingStroke ? { [getAttributeName("vector-effect")]: "non-scaling-stroke", ...attrs } : attrs;
160
- return children ? [name, nextAttrs, children] : [name, nextAttrs];
161
- })
162
- ];
163
- }
164
-
165
- // ../../node_modules/lucide-react/dist/esm/shared/src/build/buildLucideIconForReact.mjs
166
- function buildLucideIconForReact(icon, params = {}) {
167
- return buildLucideIconNode(icon, {
168
- ...params,
169
- attributeNames: {
170
- ...params.attributeNames,
171
- class: "className",
172
- "stroke-width": "strokeWidth",
173
- "stroke-linecap": "strokeLinecap",
174
- "stroke-linejoin": "strokeLinejoin",
175
- "vector-effect": "vectorEffect"
176
- }
177
- });
178
- }
179
-
180
- // ../../node_modules/lucide-react/dist/esm/shared/src/utils/hasA11yProp.mjs
181
- var hasA11yProp = (props) => {
182
- for (const prop in props) {
183
- if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
184
- return true;
185
- }
186
- }
187
- return false;
188
- };
189
-
190
- // ../../node_modules/lucide-react/dist/esm/context.mjs
191
- import { createContext, useContext, useMemo, createElement } from "react";
192
- var LucideContext = createContext({});
193
- var useLucideContext = () => useContext(LucideContext);
194
-
195
- // ../../node_modules/lucide-react/dist/esm/Icon.mjs
196
- var Icon = forwardRef(
197
- ({
198
- color,
199
- size,
200
- width,
201
- height,
202
- strokeWidth,
203
- absoluteStrokeWidth,
204
- nonScalingStroke,
205
- className = "",
206
- children,
207
- iconNode = [],
208
- icon = {
209
- node: iconNode,
210
- aliases: [],
211
- size: 24
212
- },
213
- ...rest
214
- }, ref) => {
215
- const {
216
- size: contextSize = 24,
217
- strokeWidth: contextStrokeWidth = 2,
218
- absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
219
- nonScalingStroke: contextNonScalingStroke = false,
220
- color: contextColor = "currentColor",
221
- className: contextClass = ""
222
- } = useLucideContext() ?? {};
223
- const hasAccessibleProp = Boolean(children) || hasA11yProp(rest);
224
- const [name, svgAttributes, builtIconNode = []] = buildLucideIconForReact(icon, {
225
- color: color ?? contextColor,
226
- width: width ?? size ?? contextSize,
227
- height: height ?? size ?? contextSize,
228
- strokeWidth: strokeWidth ?? contextStrokeWidth,
229
- absoluteStrokeWidth: absoluteStrokeWidth ?? contextAbsoluteStrokeWidth,
230
- nonScalingStroke: nonScalingStroke ?? contextNonScalingStroke,
231
- className: mergeClasses(contextClass, className),
232
- hasA11yProp: hasAccessibleProp,
233
- attributes: rest
234
- });
235
- return createElement2(
236
- name,
237
- {
238
- ref,
239
- ...svgAttributes
240
- },
241
- [
242
- ...builtIconNode.map(([tag, attrs]) => createElement2(tag, attrs)),
243
- ...Array.isArray(children) ? children : [children]
244
- ]
245
- );
246
- }
247
- );
248
-
249
- // ../../node_modules/lucide-react/dist/esm/createLucideIcon.mjs
250
- function createLucideIcon(iconDataOrName, iconNode = [], aliases = []) {
251
- const iconData = typeof iconDataOrName === "string" ? toLucideIconData(iconDataOrName, iconNode, aliases) : iconDataOrName;
252
- const Component = forwardRef2(
253
- ({ className, ...props }, ref) => createElement3(Icon, {
254
- ref,
255
- icon: iconData,
256
- className,
257
- ...props
258
- })
259
- );
260
- if (iconData.name) {
261
- Component.displayName = toPascalCase(iconData.name);
262
- }
263
- return Component;
264
- }
265
-
266
- // ../../node_modules/lucide-react/dist/esm/icons/activity.mjs
267
- var __iconData = {
268
- name: "activity",
269
- size: 24,
270
- node: [
271
- [
272
- "path",
273
- {
274
- d: "M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",
275
- key: "169zse"
276
- }
277
- ]
278
- ]
279
- };
280
- __iconData.node;
281
- var Activity = createLucideIcon(__iconData);
282
-
283
- // ../../node_modules/lucide-react/dist/esm/icons/app-window.mjs
284
- var __iconData2 = {
285
- name: "app-window",
286
- size: 24,
287
- node: [
288
- ["rect", { x: "2", y: "4", width: "20", height: "16", rx: "2", key: "izxlao" }],
289
- ["path", { d: "M10 4v4", key: "pp8u80" }],
290
- ["path", { d: "M2 8h20", key: "d11cs7" }],
291
- ["path", { d: "M6 4v4", key: "1svtjw" }]
292
- ]
293
- };
294
- __iconData2.node;
295
- var AppWindow = createLucideIcon(__iconData2);
296
-
297
- // ../../node_modules/lucide-react/dist/esm/icons/arrow-down.mjs
298
- var __iconData3 = {
299
- name: "arrow-down",
300
- size: 24,
301
- node: [
302
- ["path", { d: "M12 5v14", key: "s699le" }],
303
- ["path", { d: "m19 12-7 7-7-7", key: "1idqje" }]
304
- ]
305
- };
306
- __iconData3.node;
307
- var ArrowDown = createLucideIcon(__iconData3);
308
-
309
- // ../../node_modules/lucide-react/dist/esm/icons/arrow-up-right.mjs
310
- var __iconData4 = {
311
- name: "arrow-up-right",
312
- size: 24,
313
- node: [
314
- ["path", { d: "M7 7h10v10", key: "1tivn9" }],
315
- ["path", { d: "M7 17 17 7", key: "1vkiza" }]
316
- ]
317
- };
318
- __iconData4.node;
319
- var ArrowUpRight = createLucideIcon(__iconData4);
320
-
321
- // ../../node_modules/lucide-react/dist/esm/icons/arrow-up.mjs
322
- var __iconData5 = {
323
- name: "arrow-up",
324
- size: 24,
325
- node: [
326
- ["path", { d: "m5 12 7-7 7 7", key: "hav0vg" }],
327
- ["path", { d: "M12 19V5", key: "x0mq9r" }]
328
- ]
329
- };
330
- __iconData5.node;
331
- var ArrowUp = createLucideIcon(__iconData5);
332
-
333
- // ../../node_modules/lucide-react/dist/esm/icons/book-open.mjs
334
- var __iconData6 = {
335
- name: "book-open",
336
- size: 24,
337
- node: [
338
- ["path", { d: "M12 5v16", key: "1f6ucr" }],
339
- [
340
- "path",
341
- {
342
- d: "M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z",
343
- key: "1fyvmf"
344
- }
345
- ]
346
- ]
347
- };
348
- __iconData6.node;
349
- var BookOpen = createLucideIcon(__iconData6);
350
-
351
- // ../../node_modules/lucide-react/dist/esm/icons/check.mjs
352
- var __iconData7 = {
353
- name: "check",
354
- size: 24,
355
- node: [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]]
356
- };
357
- __iconData7.node;
358
- var Check = createLucideIcon(__iconData7);
359
-
360
- // ../../node_modules/lucide-react/dist/esm/icons/chevron-down.mjs
361
- var __iconData8 = {
362
- name: "chevron-down",
363
- size: 24,
364
- node: [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]]
365
- };
366
- __iconData8.node;
367
- var ChevronDown = createLucideIcon(__iconData8);
368
-
369
- // ../../node_modules/lucide-react/dist/esm/icons/chevron-right.mjs
370
- var __iconData9 = {
371
- name: "chevron-right",
372
- size: 24,
373
- node: [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]]
374
- };
375
- __iconData9.node;
376
- var ChevronRight = createLucideIcon(__iconData9);
377
-
378
- // ../../node_modules/lucide-react/dist/esm/icons/circle-check.mjs
379
- var __iconData10 = {
380
- name: "circle-check",
381
- size: 24,
382
- node: [
383
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
384
- ["path", { d: "m16 9-5.5 5.5L8 12", key: "xofnsj" }]
385
- ],
386
- aliases: ["check-circle-2"]
387
- };
388
- __iconData10.node;
389
- var CircleCheck = createLucideIcon(__iconData10);
390
-
391
- // ../../node_modules/lucide-react/dist/esm/icons/circle-slash.mjs
392
- var __iconData11 = {
393
- name: "circle-slash",
394
- size: 24,
395
- node: [
396
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
397
- ["line", { x1: "9", x2: "15", y1: "15", y2: "9", key: "1dfufj" }]
398
- ]
399
- };
400
- __iconData11.node;
401
- var CircleSlash = createLucideIcon(__iconData11);
402
-
403
- // ../../node_modules/lucide-react/dist/esm/icons/circle-x.mjs
404
- var __iconData12 = {
405
- name: "circle-x",
406
- size: 24,
407
- node: [
408
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
409
- ["path", { d: "m15 9-6 6", key: "1uzhvr" }],
410
- ["path", { d: "m9 9 6 6", key: "z0biqf" }]
411
- ],
412
- aliases: ["x-circle"]
413
- };
414
- __iconData12.node;
415
- var CircleX = createLucideIcon(__iconData12);
416
-
417
- // ../../node_modules/lucide-react/dist/esm/icons/clock.mjs
418
- var __iconData13 = {
419
- name: "clock",
420
- size: 24,
421
- node: [
422
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
423
- ["path", { d: "M12 6v6l4 2", key: "mmk7yg" }]
424
- ]
425
- };
426
- __iconData13.node;
427
- var Clock = createLucideIcon(__iconData13);
428
-
429
- // ../../node_modules/lucide-react/dist/esm/icons/coins.mjs
430
- var __iconData14 = {
431
- name: "coins",
432
- size: 24,
433
- node: [
434
- ["path", { d: "M13.744 17.736a6 6 0 1 1-7.48-7.48", key: "bq4yh3" }],
435
- ["path", { d: "M15 6h1v4", key: "11y1tn" }],
436
- ["path", { d: "m6.134 14.768.866-.5 2 3.464", key: "17snzx" }],
437
- ["circle", { cx: "16", cy: "8", r: "6", key: "14bfc9" }]
438
- ]
439
- };
440
- __iconData14.node;
441
- var Coins = createLucideIcon(__iconData14);
442
-
443
- // ../../node_modules/lucide-react/dist/esm/icons/copy.mjs
444
- var __iconData15 = {
445
- name: "copy",
446
- size: 24,
447
- node: [
448
- ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
449
- ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
450
- ]
451
- };
452
- __iconData15.node;
453
- var Copy = createLucideIcon(__iconData15);
454
-
455
- // ../../node_modules/lucide-react/dist/esm/icons/cpu.mjs
456
- var __iconData16 = {
457
- name: "cpu",
458
- size: 24,
459
- node: [
460
- ["path", { d: "M12 20v2", key: "1lh1kg" }],
461
- ["path", { d: "M12 2v2", key: "tus03m" }],
462
- ["path", { d: "M17 20v2", key: "1rnc9c" }],
463
- ["path", { d: "M17 2v2", key: "11trls" }],
464
- ["path", { d: "M2 12h2", key: "1t8f8n" }],
465
- ["path", { d: "M2 17h2", key: "7oei6x" }],
466
- ["path", { d: "M2 7h2", key: "asdhe0" }],
467
- ["path", { d: "M20 12h2", key: "1q8mjw" }],
468
- ["path", { d: "M20 17h2", key: "1fpfkl" }],
469
- ["path", { d: "M20 7h2", key: "1o8tra" }],
470
- ["path", { d: "M7 20v2", key: "4gnj0m" }],
471
- ["path", { d: "M7 2v2", key: "1i4yhu" }],
472
- ["rect", { x: "4", y: "4", width: "16", height: "16", rx: "2", key: "1vbyd7" }],
473
- ["rect", { x: "8", y: "8", width: "8", height: "8", rx: "1", key: "z9xiuo" }]
474
- ]
475
- };
476
- __iconData16.node;
477
- var Cpu = createLucideIcon(__iconData16);
478
-
479
- // ../../node_modules/lucide-react/dist/esm/icons/crosshair.mjs
480
- var __iconData17 = {
481
- name: "crosshair",
482
- size: 24,
483
- node: [
484
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
485
- ["line", { x1: "22", x2: "18", y1: "12", y2: "12", key: "l9bcsi" }],
486
- ["line", { x1: "6", x2: "2", y1: "12", y2: "12", key: "13hhkx" }],
487
- ["line", { x1: "12", x2: "12", y1: "6", y2: "2", key: "10w3f3" }],
488
- ["line", { x1: "12", x2: "12", y1: "22", y2: "18", key: "15g9kq" }]
489
- ]
490
- };
491
- __iconData17.node;
492
- var Crosshair = createLucideIcon(__iconData17);
493
-
494
- // ../../node_modules/lucide-react/dist/esm/icons/external-link.mjs
495
- var __iconData18 = {
496
- name: "external-link",
497
- size: 24,
498
- node: [
499
- ["path", { d: "M15 3h6v6", key: "1q9fwt" }],
500
- ["path", { d: "M10 14 21 3", key: "gplh6r" }],
501
- ["path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6", key: "a6xqqp" }]
502
- ]
503
- };
504
- __iconData18.node;
505
- var ExternalLink = createLucideIcon(__iconData18);
506
-
507
- // ../../node_modules/lucide-react/dist/esm/icons/file-pen-line.mjs
508
- var __iconData19 = {
509
- name: "file-pen-line",
510
- size: 24,
511
- node: [
512
- [
513
- "path",
514
- {
515
- d: "M14.364 13.634a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506l4.013-4.009a1 1 0 0 0-3.004-3.004z",
516
- key: "ukzhwg"
517
- }
518
- ],
519
- ["path", { d: "M14.487 7.858A1 1 0 0 1 14 7V2", key: "1klhew" }],
520
- [
521
- "path",
522
- {
523
- d: "M20 19.645V20a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l2.516 2.516",
524
- key: "rxaxab"
525
- }
526
- ],
527
- ["path", { d: "M8 18h1", key: "13wk12" }]
528
- ],
529
- aliases: ["file-signature"]
530
- };
531
- __iconData19.node;
532
- var FilePenLine = createLucideIcon(__iconData19);
533
-
534
- // ../../node_modules/lucide-react/dist/esm/icons/file-spreadsheet.mjs
535
- var __iconData20 = {
536
- name: "file-spreadsheet",
537
- size: 24,
538
- node: [
539
- [
540
- "path",
541
- {
542
- d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
543
- key: "1oefj6"
544
- }
545
- ],
546
- ["path", { d: "M14 2v5a1 1 0 0 0 1 1h5", key: "wfsgrz" }],
547
- ["path", { d: "M8 13h2", key: "yr2amv" }],
548
- ["path", { d: "M14 13h2", key: "un5t4a" }],
549
- ["path", { d: "M8 17h2", key: "2yhykz" }],
550
- ["path", { d: "M14 17h2", key: "10kma7" }]
551
- ]
552
- };
553
- __iconData20.node;
554
- var FileSpreadsheet = createLucideIcon(__iconData20);
555
-
556
- // ../../node_modules/lucide-react/dist/esm/icons/file-text.mjs
557
- var __iconData21 = {
558
- name: "file-text",
559
- size: 24,
560
- node: [
561
- [
562
- "path",
563
- {
564
- d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
565
- key: "1oefj6"
566
- }
567
- ],
568
- ["path", { d: "M14 2v5a1 1 0 0 0 1 1h5", key: "wfsgrz" }],
569
- ["path", { d: "M10 9H8", key: "b1mrlr" }],
570
- ["path", { d: "M16 13H8", key: "t4e002" }],
571
- ["path", { d: "M16 17H8", key: "z1uh3a" }]
572
- ]
573
- };
574
- __iconData21.node;
575
- var FileText = createLucideIcon(__iconData21);
576
-
577
- // ../../node_modules/lucide-react/dist/esm/icons/folder.mjs
578
- var __iconData22 = {
579
- name: "folder",
580
- size: 24,
581
- node: [
582
- [
583
- "path",
584
- {
585
- d: "M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",
586
- key: "1kt360"
587
- }
588
- ]
589
- ]
590
- };
591
- __iconData22.node;
592
- var Folder = createLucideIcon(__iconData22);
593
-
594
- // ../../node_modules/lucide-react/dist/esm/icons/gauge.mjs
595
- var __iconData23 = {
596
- name: "gauge",
597
- size: 24,
598
- node: [
599
- ["path", { d: "m12 14 4-4", key: "9kzdfg" }],
600
- ["path", { d: "M3.34 19a10 10 0 1 1 17.32 0", key: "19p75a" }]
601
- ]
602
- };
603
- __iconData23.node;
604
- var Gauge = createLucideIcon(__iconData23);
605
-
606
- // ../../node_modules/lucide-react/dist/esm/icons/globe.mjs
607
- var __iconData24 = {
608
- name: "globe",
609
- size: 24,
610
- node: [
611
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
612
- ["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20", key: "13o1zl" }],
613
- ["path", { d: "M2 12h20", key: "9i4pu4" }]
614
- ]
615
- };
616
- __iconData24.node;
617
- var Globe = createLucideIcon(__iconData24);
618
-
619
- // ../../node_modules/lucide-react/dist/esm/icons/image.mjs
620
- var __iconData25 = {
621
- name: "image",
622
- size: 24,
623
- node: [
624
- ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
625
- ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
626
- ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
627
- ]
628
- };
629
- __iconData25.node;
630
- var Image = createLucideIcon(__iconData25);
631
-
632
- // ../../node_modules/lucide-react/dist/esm/icons/inbox.mjs
633
- var __iconData26 = {
634
- name: "inbox",
635
- size: 24,
636
- node: [
637
- ["polyline", { points: "22 12 16 12 14 15 10 15 8 12 2 12", key: "o97t9d" }],
638
- [
639
- "path",
640
- {
641
- d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",
642
- key: "oot6mr"
643
- }
644
- ]
645
- ]
646
- };
647
- __iconData26.node;
648
- var Inbox = createLucideIcon(__iconData26);
649
-
650
- // ../../node_modules/lucide-react/dist/esm/icons/list-checks.mjs
651
- var __iconData27 = {
652
- name: "list-checks",
653
- size: 24,
654
- node: [
655
- ["path", { d: "M13 5h8", key: "a7qcls" }],
656
- ["path", { d: "M13 12h8", key: "h98zly" }],
657
- ["path", { d: "M13 19h8", key: "c3s6r1" }],
658
- ["path", { d: "m3 17 2 2 4-4", key: "1jhpwq" }],
659
- ["path", { d: "m3 7 2 2 4-4", key: "1obspn" }]
660
- ]
661
- };
662
- __iconData27.node;
663
- var ListChecks = createLucideIcon(__iconData27);
664
-
665
- // ../../node_modules/lucide-react/dist/esm/icons/loader-circle.mjs
666
- var __iconData28 = {
667
- name: "loader-circle",
668
- size: 24,
669
- node: [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]],
670
- aliases: ["loader-2"]
671
- };
672
- __iconData28.node;
673
- var LoaderCircle = createLucideIcon(__iconData28);
674
-
675
- // ../../node_modules/lucide-react/dist/esm/icons/mic.mjs
676
- var __iconData29 = {
677
- name: "mic",
678
- size: 24,
679
- node: [
680
- ["path", { d: "M12 19v3", key: "npa21l" }],
681
- ["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
682
- ["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
683
- ]
684
- };
685
- __iconData29.node;
686
- var Mic = createLucideIcon(__iconData29);
687
-
688
- // ../../node_modules/lucide-react/dist/esm/icons/octagon-x.mjs
689
- var __iconData30 = {
690
- name: "octagon-x",
691
- size: 24,
692
- node: [
693
- ["path", { d: "m15 9-6 6", key: "1uzhvr" }],
694
- [
695
- "path",
696
- {
697
- d: "M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z",
698
- key: "2d38gg"
699
- }
700
- ],
701
- ["path", { d: "m9 9 6 6", key: "z0biqf" }]
702
- ],
703
- aliases: ["x-octagon"]
704
- };
705
- __iconData30.node;
706
- var OctagonX = createLucideIcon(__iconData30);
707
-
708
- // ../../node_modules/lucide-react/dist/esm/icons/paperclip.mjs
709
- var __iconData31 = {
710
- name: "paperclip",
711
- size: 24,
712
- node: [
713
- [
714
- "path",
715
- {
716
- d: "m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551",
717
- key: "1miecu"
718
- }
719
- ]
720
- ]
721
- };
722
- __iconData31.node;
723
- var Paperclip = createLucideIcon(__iconData31);
724
-
725
- // ../../node_modules/lucide-react/dist/esm/icons/rotate-ccw.mjs
726
- var __iconData32 = {
727
- name: "rotate-ccw",
728
- size: 24,
729
- node: [
730
- ["path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8", key: "1357e3" }],
731
- ["path", { d: "M3 3v5h5", key: "1xhq8a" }]
732
- ]
733
- };
734
- __iconData32.node;
735
- var RotateCcw = createLucideIcon(__iconData32);
736
-
737
- // ../../node_modules/lucide-react/dist/esm/icons/search.mjs
738
- var __iconData33 = {
739
- name: "search",
740
- size: 24,
741
- node: [
742
- ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
743
- ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
744
- ]
745
- };
746
- __iconData33.node;
747
- var Search = createLucideIcon(__iconData33);
748
-
749
- // ../../node_modules/lucide-react/dist/esm/icons/send.mjs
750
- var __iconData34 = {
751
- name: "send",
752
- size: 24,
753
- node: [
754
- [
755
- "path",
756
- {
757
- d: "M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",
758
- key: "1ffxy3"
759
- }
760
- ],
761
- ["path", { d: "m21.854 2.147-10.94 10.939", key: "12cjpa" }]
762
- ]
763
- };
764
- __iconData34.node;
765
- var Send = createLucideIcon(__iconData34);
766
-
767
- // ../../node_modules/lucide-react/dist/esm/icons/shield-alert.mjs
768
- var __iconData35 = {
769
- name: "shield-alert",
770
- size: 24,
771
- node: [
772
- [
773
- "path",
774
- {
775
- d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",
776
- key: "oel41y"
777
- }
778
- ],
779
- ["path", { d: "M12 8v4", key: "1got3b" }],
780
- ["path", { d: "M12 16h.01", key: "1drbdi" }]
781
- ]
782
- };
783
- __iconData35.node;
784
- var ShieldAlert = createLucideIcon(__iconData35);
785
-
786
- // ../../node_modules/lucide-react/dist/esm/icons/square.mjs
787
- var __iconData36 = {
788
- name: "square",
789
- size: 24,
790
- node: [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]]
791
- };
792
- __iconData36.node;
793
- var Square = createLucideIcon(__iconData36);
794
-
795
- // ../../node_modules/lucide-react/dist/esm/icons/users-round.mjs
796
- var __iconData37 = {
797
- name: "users-round",
798
- size: 24,
799
- node: [
800
- ["path", { d: "M18 21a8 8 0 0 0-16 0", key: "3ypg7q" }],
801
- ["circle", { cx: "10", cy: "8", r: "5", key: "o932ke" }],
802
- ["path", { d: "M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3", key: "10s06x" }]
803
- ],
804
- aliases: ["users-2"]
805
- };
806
- __iconData37.node;
807
- var UsersRound = createLucideIcon(__iconData37);
808
-
809
- // ../../node_modules/lucide-react/dist/esm/icons/x.mjs
810
- var __iconData38 = {
811
- name: "x",
812
- size: 24,
813
- node: [
814
- ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
815
- ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
816
- ]
817
- };
818
- __iconData38.node;
819
- var X = createLucideIcon(__iconData38);
820
-
821
101
  // src/react/Piloto.tsx
822
102
  import {
823
- forwardRef as forwardRef3,
824
- useCallback as useCallback3,
825
- useEffect as useEffect6,
103
+ forwardRef,
104
+ useCallback as useCallback2,
105
+ useEffect as useEffect5,
826
106
  useImperativeHandle,
827
107
  useLayoutEffect,
828
- useMemo as useMemo4,
829
- useRef as useRef5,
830
- useState as useState9
108
+ useMemo as useMemo3,
109
+ useRef as useRef4,
110
+ useState as useState8
831
111
  } from "react";
832
112
 
833
113
  // src/react/Bubble.tsx
@@ -835,184 +115,6 @@ import { useState as useState5 } from "react";
835
115
 
836
116
  // src/react/ConfirmCard.tsx
837
117
  import { useEffect, useRef, useState } from "react";
838
-
839
- // src/react/context.tsx
840
- import { createContext as createContext2, useContext as useContext2 } from "react";
841
-
842
- // src/react/strings.ts
843
- var STRINGS_ES = {
844
- numberLocale: "es-MX",
845
- placeholder: (agent) => `Escr\xEDbele a ${agent}`,
846
- placeholderDefault: "Escr\xEDbele al agente",
847
- send: "Enviar",
848
- stop: "Detener",
849
- uploading: "Subiendo tu archivo\u2026",
850
- uploadingN: (n) => `Subiendo tus ${n} archivos\u2026`,
851
- attachImage: "Adjuntar imagen",
852
- attachFile: "Adjuntar archivo",
853
- noImages: "Este agente no ve im\xE1genes",
854
- removeAttachment: "Quitar adjunto",
855
- removeReference: "Quitar referencia",
856
- dropFiles: "Suelta tus archivos",
857
- dropHint: "Excel, Word, PDF o im\xE1genes",
858
- tooBig: (name, mb) => `${name} pesa m\xE1s de ${mb} MB`,
859
- tooMany: (max) => `no caben m\xE1s de ${max} archivos por mensaje`,
860
- cantSeeImage: (name) => `${name}: este agente no ve im\xE1genes`,
861
- cantUpload: (name) => `${name}: aqu\xED s\xF3lo se pueden mandar im\xE1genes y texto`,
862
- uploadFailed: "no se pudo subir el archivo",
863
- recordVoice: "Grabar nota de voz",
864
- voiceNote: "Nota de voz",
865
- cancelVoice: "Cancelar la nota de voz",
866
- stopAndSendVoice: "Parar y mandar la nota de voz",
867
- micDenied: "No tengo permiso para usar el micr\xF3fono.",
868
- voiceEmpty: "no se entendi\xF3 nada en la nota de voz",
869
- voiceFailed: "no se pudo mandar la nota de voz",
870
- model: "Modelo",
871
- thinking: "Pensando\u2026",
872
- howItGoes: (s) => s < 6 ? "Pensando\u2026" : s < 15 ? "Trabajando\u2026" : s < 60 ? "Estoy en ello\u2026" : s < 120 ? "Esto me lleva un poco\u2026" : s < 300 ? "Llevo un buen rato\u2026" : "Me est\xE1 tomando mucho \u2014 puedes detenerme",
873
- stillWorking: "Estoy en ello\u2026",
874
- retry: "Reintentar",
875
- copy: "Copiar",
876
- copied: "Copiado",
877
- copyMessage: "Copiar mensaje",
878
- worked: (s) => `Trabaj\xF3 ${s} s`,
879
- stepsExpand: "Ver el detalle de los pasos",
880
- stepsCollapse: "Plegar los pasos",
881
- earlierSteps: (n) => n === 1 ? "1 paso antes" : `${n} pasos antes`,
882
- withProblems: (n) => n === 1 ? "1 con problemas" : `${n} con problemas`,
883
- running: "Ejecutando",
884
- done: "Lista",
885
- failed: "Fall\xF3",
886
- step: "Paso",
887
- replyOptions: "Opciones para responder",
888
- linkTitle: "Vas a salir de aqu\xED",
889
- linkCopy: "Copiar",
890
- linkCopied: "Copiado",
891
- linkOpen: "Abrir",
892
- close: "Cerrar",
893
- goToLatest: "Ir al \xFAltimo mensaje",
894
- steps: { waking: "Despertando al agente\u2026", session: "Preparando la sesi\xF3n\u2026", compacting: "Resumiendo la conversaci\xF3n\u2026" },
895
- loadOlder: "Cargar mensajes anteriores",
896
- loadingOlder: "Cargando\u2026",
897
- continueRun: "Continuar",
898
- runCut: "La respuesta se cort\xF3 antes de terminar.",
899
- stalled: "Se ator\xF3, sigo intentando\u2026",
900
- truncated: "La respuesta qued\xF3 incompleta.",
901
- attachmentFailed: (name, reason) => reason ? `No pude leer ${name}: ${reason}` : `No pude leer ${name}`,
902
- queued: "Se manda cuando termine",
903
- subagentsWorking: (n) => n === 1 ? "1 agente trabajando" : `${n} agentes trabajando`,
904
- subagentsDone: (n) => n === 1 ? "1 agente termin\xF3" : `${n} agentes terminaron`,
905
- subagentStop: "Detener",
906
- subagentStatus: { running: "Trabajando", completed: "Termin\xF3", failed: "Fall\xF3", stopped: "Detenido" },
907
- confirmTitle: "Necesito tu permiso para",
908
- approve: "Aprobar",
909
- reject: "Rechazar",
910
- expiresIn: (t) => `Vence en ${t}`,
911
- expiredNow: "Venci\xF3 sin respuesta",
912
- decisionApproved: "Aprobaste",
913
- decisionRejected: "Rechazaste",
914
- decisionExpired: "Venci\xF3",
915
- decisionCancelled: "Descartaste",
916
- stampCancelled: "Descartado",
917
- stampApproved: "Aprobado",
918
- stampRejected: "Rechazado",
919
- stampExpired: "Vencido"
920
- };
921
- var STRINGS_EN = {
922
- numberLocale: "en-US",
923
- placeholder: (agent) => `Message ${agent}`,
924
- placeholderDefault: "Message the agent",
925
- send: "Send",
926
- stop: "Stop",
927
- uploading: "Uploading your file\u2026",
928
- uploadingN: (n) => `Uploading your ${n} files\u2026`,
929
- attachImage: "Attach image",
930
- attachFile: "Attach file",
931
- noImages: "This agent can't see images",
932
- removeAttachment: "Remove attachment",
933
- removeReference: "Remove reference",
934
- dropFiles: "Drop your files",
935
- dropHint: "Excel, Word, PDF or images",
936
- tooBig: (name, mb) => `${name} is larger than ${mb} MB`,
937
- tooMany: (max) => `no more than ${max} files per message`,
938
- cantSeeImage: (name) => `${name}: this agent can't see images`,
939
- cantUpload: (name) => `${name}: only images and text can be sent here`,
940
- uploadFailed: "couldn't upload the file",
941
- recordVoice: "Record voice note",
942
- voiceNote: "Voice note",
943
- cancelVoice: "Cancel voice note",
944
- stopAndSendVoice: "Stop and send voice note",
945
- micDenied: "I don't have permission to use the microphone.",
946
- voiceEmpty: "nothing was understood in the voice note",
947
- voiceFailed: "couldn't send the voice note",
948
- model: "Model",
949
- thinking: "Thinking\u2026",
950
- howItGoes: (s) => s < 6 ? "Thinking\u2026" : s < 15 ? "Working\u2026" : s < 60 ? "On it\u2026" : s < 120 ? "This is taking a bit\u2026" : s < 300 ? "Been at it a while\u2026" : "This is taking long \u2014 you can stop me",
951
- stillWorking: "On it\u2026",
952
- retry: "Retry",
953
- copy: "Copy",
954
- copied: "Copied",
955
- copyMessage: "Copy message",
956
- worked: (s) => `Worked ${s} s`,
957
- stepsExpand: "See step details",
958
- stepsCollapse: "Collapse the steps",
959
- earlierSteps: (n) => n === 1 ? "1 earlier step" : `${n} earlier steps`,
960
- withProblems: (n) => `${n} with problems`,
961
- running: "Running",
962
- done: "Done",
963
- failed: "Failed",
964
- step: "Step",
965
- replyOptions: "Reply options",
966
- linkTitle: "You're about to leave",
967
- linkCopy: "Copy",
968
- linkCopied: "Copied",
969
- linkOpen: "Open",
970
- close: "Close",
971
- goToLatest: "Go to latest message",
972
- steps: { waking: "Waking the agent\u2026", session: "Starting the session\u2026", compacting: "Compacting the conversation\u2026" },
973
- loadOlder: "Load earlier messages",
974
- loadingOlder: "Loading\u2026",
975
- continueRun: "Continue",
976
- runCut: "The answer was cut off before it finished.",
977
- stalled: "Got stuck, still trying\u2026",
978
- truncated: "The answer is incomplete.",
979
- attachmentFailed: (name, reason) => reason ? `Couldn't read ${name}: ${reason}` : `Couldn't read ${name}`,
980
- queued: "Sends when this finishes",
981
- subagentsWorking: (n) => n === 1 ? "1 agent working" : `${n} agents working`,
982
- subagentsDone: (n) => n === 1 ? "1 agent finished" : `${n} agents finished`,
983
- subagentStop: "Stop",
984
- subagentStatus: { running: "Working", completed: "Done", failed: "Failed", stopped: "Stopped" },
985
- confirmTitle: "I need your permission to",
986
- approve: "Approve",
987
- reject: "Reject",
988
- expiresIn: (t) => `Expires in ${t}`,
989
- expiredNow: "Expired without an answer",
990
- decisionApproved: "You approved",
991
- decisionRejected: "You rejected",
992
- decisionExpired: "Expired",
993
- decisionCancelled: "You dismissed",
994
- stampCancelled: "Dismissed",
995
- stampApproved: "Approved",
996
- stampRejected: "Rejected",
997
- stampExpired: "Expired"
998
- };
999
-
1000
- // src/react/context.tsx
1001
- var PilotoContext = createContext2({
1002
- strings: STRINGS_ES,
1003
- toolVerbs: TOOL_VERBS_ES,
1004
- toolIcons: {},
1005
- formatUserText: cleanUserText,
1006
- theme: "light",
1007
- skin: "ghosty",
1008
- markdown: {}
1009
- });
1010
- var usePiloto = () => useContext2(PilotoContext);
1011
- function rootProps(theme, skin = "ghosty") {
1012
- return { className: "piloto", "data-theme": theme, "data-skin": skin };
1013
- }
1014
-
1015
- // src/react/ConfirmCard.tsx
1016
118
  import { jsx, jsxs } from "react/jsx-runtime";
1017
119
  var isRejectOption = (o) => /^reject|deny|cancel/i.test(o.kind ?? o.optionId);
1018
120
  var mmss = (ms) => {
@@ -1101,10 +203,10 @@ function DecisionLines({ decisions }) {
1101
203
  const { strings: t } = usePiloto();
1102
204
  if (!decisions.length) return null;
1103
205
  return /* @__PURE__ */ jsx("ul", { className: "piloto-decisions", children: decisions.map((d) => {
1104
- const Icon2 = d.status === "approved" ? CircleCheck : d.status === "expired" ? Clock : CircleSlash;
206
+ const Icon = d.status === "approved" ? CircleCheck : d.status === "expired" ? Clock : CircleSlash;
1105
207
  const label = d.status === "approved" ? t.decisionApproved : d.status === "rejected" ? t.decisionRejected : d.status === "cancelled" ? t.decisionCancelled : t.decisionExpired;
1106
208
  return /* @__PURE__ */ jsxs("li", { className: "piloto-decision", "data-status": d.status, children: [
1107
- /* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4 shrink-0", "aria-hidden": true }),
209
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0", "aria-hidden": true }),
1108
210
  /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
1109
211
  /* @__PURE__ */ jsxs("strong", { children: [
1110
212
  label,
@@ -1123,11 +225,11 @@ function DecisionLines({ decisions }) {
1123
225
  }
1124
226
 
1125
227
  // src/react/Markdown.tsx
1126
- import { useState as useState3 } from "react";
228
+ import { useContext as useContext2, useState as useState3 } from "react";
1127
229
  import { Streamdown } from "streamdown";
1128
230
 
1129
231
  // src/react/TextAtPace.tsx
1130
- import { createContext as createContext3, memo, useContext as useContext3, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
232
+ import { createContext, memo, useContext, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
1131
233
  import { Block } from "streamdown";
1132
234
  import { jsx as jsx2 } from "react/jsx-runtime";
1133
235
  var CHUNK_MS = 400;
@@ -1195,9 +297,9 @@ function useTextAtPace(text, live, whole = false) {
1195
297
  if (!animate.current) return { visible: text, animate: false };
1196
298
  return { visible: text.slice(0, Math.min(released, text.length)), animate: true };
1197
299
  }
1198
- var ChunksContext = createContext3(null);
300
+ var ChunksContext = createContext(null);
1199
301
  function useChunkRegistry() {
1200
- return useMemo2(() => /* @__PURE__ */ new Map(), []);
302
+ return useMemo(() => /* @__PURE__ */ new Map(), []);
1201
303
  }
1202
304
  var UNTOUCHED = /* @__PURE__ */ new Set(["pre", "code", "svg", "math", "script", "style"]);
1203
305
  var STYLE = `gs-tramo ${CHUNK_MS}ms linear both`;
@@ -1253,9 +355,9 @@ function chunksPlugin(state) {
1253
355
  };
1254
356
  }
1255
357
  var ChunkedBlock = memo(function ChunkedBlock2(props) {
1256
- const ctx = useContext3(ChunksContext);
358
+ const ctx = useContext(ChunksContext);
1257
359
  const key = `${ctx?.part ?? 0}-${props.index}`;
1258
- const plugins = useMemo2(() => {
360
+ const plugins = useMemo(() => {
1259
361
  if (!ctx) return props.rehypePlugins;
1260
362
  let state = ctx.registry.get(key);
1261
363
  if (!state) {
@@ -1323,7 +425,8 @@ var linkSafety = { enabled: true, renderModal: (p) => /* @__PURE__ */ jsx3(LinkS
1323
425
  function Markdown({ children, animate = false, className }) {
1324
426
  const { markdown } = usePiloto();
1325
427
  const registry = useChunkRegistry();
1326
- const text = markdown.transform ? markdown.transform(children) : children;
428
+ const turn = useContext2(TurnContext);
429
+ const text = markdown.transform ? markdown.transform(children, turn) : children;
1327
430
  const onClick = markdown.onLinkClick ? (e) => {
1328
431
  const a = e.target.closest("a[href]");
1329
432
  if (!a) return;
@@ -1339,7 +442,7 @@ function Markdown({ children, animate = false, className }) {
1339
442
  {
1340
443
  onClickCapture: onClick,
1341
444
  className: `ghosty-md min-w-0 max-w-full break-words ${className ?? "text-text-primary"} [&_pre]:max-w-full [&_pre]:overflow-x-auto [&_table]:block [&_table]:max-w-full [&_table]:overflow-x-auto`,
1342
- children: animate ? /* @__PURE__ */ jsx3(ChunksContext.Provider, { value: { registry, part: 0 }, children: /* @__PURE__ */ jsx3(Streamdown, { BlockComponent: ChunkedBlock, linkSafety, children: text }) }) : /* @__PURE__ */ jsx3(Streamdown, { linkSafety, children: text })
445
+ children: animate ? /* @__PURE__ */ jsx3(ChunksContext.Provider, { value: { registry, part: 0 }, children: /* @__PURE__ */ jsx3(Streamdown, { BlockComponent: ChunkedBlock, linkSafety, components: markdown.components, children: text }) }) : /* @__PURE__ */ jsx3(Streamdown, { linkSafety, components: markdown.components, children: text })
1343
446
  }
1344
447
  )
1345
448
  );
@@ -1442,7 +545,7 @@ var DEFAULT_TOOL_ICONS = {
1442
545
  };
1443
546
  function iconFor(t, icons) {
1444
547
  for (const name of toolNames(t)) if (icons[name]) return icons[name];
1445
- return t.kind && (icons[t.kind] ?? DEFAULT_TOOL_ICONS[t.kind]) || ClawIcon;
548
+ return t.kind && (icons[t.kind] ?? DEFAULT_TOOL_ICONS[t.kind]) || icons["*"] || Wrench;
1446
549
  }
1447
550
  var VISIBLE = 5;
1448
551
  function AgentSteps({ tools, live }) {
@@ -1675,9 +778,19 @@ function TurnFooter({ text, seconds, onRetry, showCopy = true, showDuration = tr
1675
778
  ] })
1676
779
  ] });
1677
780
  }
1678
- function AgentBubble({ turn, live, onRetry, onOption, showUsage = true, showCopy = true, showDuration = true, renderDeliveries }) {
781
+ function AgentBubble(props) {
782
+ const { turn, avatar } = props;
783
+ const body = /* @__PURE__ */ jsx6(AgentBody, { ...props, showUsage: props.showUsage ?? true, showCopy: props.showCopy ?? true, showDuration: props.showDuration ?? true });
784
+ return /* @__PURE__ */ jsx6(TurnContext.Provider, { value: turn, children: avatar ? /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-3", children: [
785
+ /* @__PURE__ */ jsx6("div", { className: "mt-0.5 shrink-0", children: avatar }),
786
+ body
787
+ ] }) : body });
788
+ }
789
+ function AgentBody({ turn, live, onRetry, onOption, showUsage, showCopy, showDuration, renderDeliveries, renderTools, renderBefore, blocks, narration = true }) {
1679
790
  const { strings: t } = usePiloto();
1680
- const narrated = splitNarration(turn.text ?? "", turn.cuts);
791
+ const extracted = blocks ? extractBlocks(turn.text ?? "", blocks.langs, turn.cuts) : { text: turn.text ?? "", cuts: turn.cuts, blocks: [] };
792
+ const closed = !live && !turn.tools?.some((x) => x.status === "pending");
793
+ const narrated = narration ? splitNarration(extracted.text, extracted.cuts, closed) : { steps: [], rest: extracted.text };
1681
794
  const visible = narrated.rest;
1682
795
  const options = onOption && !live && visible ? extractReplyOptions(visible) : null;
1683
796
  return /* @__PURE__ */ jsxs4("div", { className: "gc-msg-agent min-w-0 max-w-[90%]", children: [
@@ -1686,6 +799,8 @@ function AgentBubble({ turn, live, onRetry, onOption, showUsage = true, showCopy
1686
799
  /* @__PURE__ */ jsx6("p", { className: "mt-2 whitespace-pre-wrap border-l-2 border-border-secondary pl-3", children: turn.thought })
1687
800
  ] }),
1688
801
  turn.tools && turn.tools.length > 0 && /* @__PURE__ */ jsx6(AgentSteps, { tools: turn.tools, live }),
802
+ turn.tools && turn.tools.length > 0 && renderTools?.(turn, live),
803
+ renderBefore?.(turn, live),
1689
804
  narrated.steps.length > 0 && /* @__PURE__ */ jsx6(Narration, { steps: narrated.steps }),
1690
805
  options ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
1691
806
  options.before && /* @__PURE__ */ jsx6(AnswerText, { text: options.before, live }),
@@ -1694,6 +809,7 @@ function AgentBubble({ turn, live, onRetry, onOption, showUsage = true, showCopy
1694
809
  // Cada tramo nuevo tras una herramienta monta su propio ritmo (empieza de cero).
1695
810
  visible && /* @__PURE__ */ jsx6(AnswerText, { text: visible, live }, `r${narrated.steps.length}`)
1696
811
  ),
812
+ extracted.blocks.length > 0 && blocks.render(extracted.blocks, turn, live),
1697
813
  renderDeliveries?.(turn, live),
1698
814
  turn.decisions?.length ? /* @__PURE__ */ jsx6(DecisionLines, { decisions: turn.decisions }) : null,
1699
815
  showUsage && turn.usage && /* @__PURE__ */ jsx6(UsageStats, { ...turn.usage }),
@@ -1701,586 +817,13 @@ function AgentBubble({ turn, live, onRetry, onOption, showUsage = true, showCopy
1701
817
  ] });
1702
818
  }
1703
819
 
1704
- // src/react/Chips.tsx
1705
- import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1706
- function ReferenceChips({ references, onRemove, onActivate }) {
1707
- const { strings: t } = usePiloto();
1708
- if (!references.length) return null;
1709
- const pointed = references.filter((r) => r.prompt);
1710
- const plain = references.filter((r) => !r.prompt);
1711
- return /* @__PURE__ */ jsxs5(Fragment4, { children: [
1712
- pointed.map((r) => /* @__PURE__ */ jsx7("div", { className: "mb-2 flex", children: /* @__PURE__ */ jsxs5("span", { className: "inline-flex max-w-full items-center gap-1.5 rounded-full border border-brand-500/40 bg-brand-500/10 py-1 pl-2.5 pr-1 text-xs text-text-primary", children: [
1713
- /* @__PURE__ */ jsx7(Crosshair, { size: 13, className: "shrink-0 text-brand-500" }),
1714
- /* @__PURE__ */ jsx7("span", { className: "shrink-0 font-semibold", children: r.label }),
1715
- r.hint && /* @__PURE__ */ jsxs5("span", { className: "truncate text-text-secondary", children: [
1716
- "\xAB",
1717
- r.hint,
1718
- "\xBB"
1719
- ] }),
1720
- /* @__PURE__ */ jsx7(
1721
- "button",
1722
- {
1723
- type: "button",
1724
- onClick: () => onRemove(r.id),
1725
- "aria-label": t.removeReference,
1726
- className: "grid size-5 shrink-0 cursor-pointer place-items-center rounded-full text-text-tertiary hover:bg-background-secondary hover:text-text-primary",
1727
- children: /* @__PURE__ */ jsx7(X, { size: 12 })
1728
- }
1729
- )
1730
- ] }) }, r.id)),
1731
- plain.length > 0 && /* @__PURE__ */ jsx7("div", { className: "flex flex-wrap gap-1.5", children: plain.map((r) => /* @__PURE__ */ jsxs5("span", { className: "inline-flex items-center gap-1 rounded-full border border-border-primary bg-background-secondary py-0.5 pl-2 pr-1 text-xs text-text-primary", children: [
1732
- /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onActivate?.(r), className: "underline-offset-2 hover:underline", children: r.label }),
1733
- /* @__PURE__ */ jsx7(
1734
- "button",
1735
- {
1736
- type: "button",
1737
- onClick: () => onRemove(r.id),
1738
- "aria-label": t.removeReference,
1739
- className: "flex h-4 w-4 items-center justify-center rounded-full text-text-tertiary hover:bg-background-tertiary hover:text-text-primary",
1740
- children: /* @__PURE__ */ jsx7(X, { className: "h-3 w-3" })
1741
- }
1742
- )
1743
- ] }, r.id)) })
1744
- ] });
1745
- }
1746
- function Suggestions({ items, onPick, onPrefill, disabled }) {
1747
- if (!items.length) return null;
1748
- return /* @__PURE__ */ jsx7("div", { className: "mt-4 flex flex-wrap justify-center gap-2", children: items.map((c) => /* @__PURE__ */ jsx7(
1749
- "button",
1750
- {
1751
- type: "button",
1752
- onClick: () => onPrefill && /:\s*$/.test(c) ? onPrefill(c) : onPick(c),
1753
- disabled,
1754
- className: "gc-chip rounded-full bg-irongray/10 px-3.5 py-1.5 text-xs font-medium text-metal transition-colors hover:bg-irongray/20 hover:text-dark disabled:opacity-40",
1755
- children: c
1756
- },
1757
- c
1758
- )) });
1759
- }
1760
-
1761
- // src/react/Composer.tsx
1762
- import { useCallback, useEffect as useEffect3, useRef as useRef3, useState as useState6 } from "react";
1763
- import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1764
- var MAX_HEIGHT = 240;
1765
- var BY_EXTENSION = {
1766
- png: "image/png",
1767
- jpg: "image/jpeg",
1768
- jpeg: "image/jpeg",
1769
- gif: "image/gif",
1770
- webp: "image/webp",
1771
- heic: "image/heic",
1772
- pdf: "application/pdf",
1773
- csv: "text/csv",
1774
- txt: "text/plain",
1775
- md: "text/markdown",
1776
- json: "application/json"
1777
- };
1778
- function mimeOf(file) {
1779
- if (file.type) return file.type;
1780
- const ext = (file.name.match(/\.([A-Za-z0-9]+)$/)?.[1] || "").toLowerCase();
1781
- return BY_EXTENSION[ext] ?? "application/octet-stream";
1782
- }
1783
- var isText = (mime) => mime.startsWith("text/") || mime === "application/json" || mime.endsWith("+json") || mime.endsWith("+xml");
1784
- function shortSize(bytes) {
1785
- if (bytes < 1024) return `${bytes} B`;
1786
- if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
1787
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
1788
- }
1789
- function readInline(file) {
1790
- return new Promise((resolve, reject) => {
1791
- const fr = new FileReader();
1792
- fr.onload = () => {
1793
- const result = String(fr.result);
1794
- const comma = result.indexOf(",");
1795
- resolve({ name: file.name, mimeType: mimeOf(file), data: comma === -1 ? result : result.slice(comma + 1) });
1796
- };
1797
- fr.onerror = () => reject(fr.error);
1798
- fr.readAsDataURL(file);
1799
- });
1800
- }
1801
- function Composer({
1802
- onSubmit,
1803
- onStop,
1804
- busy = false,
1805
- canSteer = true,
1806
- placeholder,
1807
- initialValue = "",
1808
- autoFocus = true,
1809
- canSeeImages = true,
1810
- attachments: withFiles = false,
1811
- upload,
1812
- transcribe,
1813
- voiceNoteText,
1814
- models = [],
1815
- currentModel = null,
1816
- onModelChange,
1817
- references = [],
1818
- onRemoveReference,
1819
- onActivateReference,
1820
- extras,
1821
- maxFiles = 8,
1822
- maxFileMb = 25
1823
- }) {
1824
- const { strings: t } = usePiloto();
1825
- const [value, setValue] = useState6(initialValue);
1826
- const [files, setFiles] = useState6([]);
1827
- const [dragging, setDragging] = useState6(false);
1828
- const [notice, setNotice] = useState6(null);
1829
- const [uploading, setUploading] = useState6(false);
1830
- const ref = useRef3(null);
1831
- const fileRef = useRef3(null);
1832
- const nextId = useRef3(0);
1833
- const urls = useRef3(/* @__PURE__ */ new Set());
1834
- const releaseUrl = (url) => {
1835
- urls.current.delete(url);
1836
- URL.revokeObjectURL(url);
1837
- };
1838
- useEffect3(() => {
1839
- if (!autoFocus) return;
1840
- const id = requestAnimationFrame(() => {
1841
- const el = ref.current;
1842
- if (!el) return;
1843
- el.focus({ preventScroll: true });
1844
- el.setSelectionRange(el.value.length, el.value.length);
1845
- });
1846
- return () => cancelAnimationFrame(id);
1847
- }, [autoFocus]);
1848
- useEffect3(
1849
- () => () => {
1850
- for (const url of urls.current) URL.revokeObjectURL(url);
1851
- urls.current.clear();
1852
- },
1853
- []
1854
- );
1855
- useEffect3(() => {
1856
- const el = ref.current;
1857
- if (!el) return;
1858
- if (!value) {
1859
- el.style.height = "";
1860
- return;
1861
- }
1862
- el.style.height = "0px";
1863
- el.style.height = `${Math.min(el.scrollHeight, MAX_HEIGHT)}px`;
1864
- }, [value]);
1865
- const addFiles = useCallback(
1866
- (list) => {
1867
- if (busy) return;
1868
- const rejected = [];
1869
- setFiles((prev) => {
1870
- const next = [...prev];
1871
- for (const file of Array.from(list)) {
1872
- const mime = mimeOf(file);
1873
- if (file.size > maxFileMb * 1024 * 1024) {
1874
- rejected.push(t.tooBig(file.name, maxFileMb));
1875
- continue;
1876
- }
1877
- if (next.length >= maxFiles) {
1878
- rejected.push(t.tooMany(maxFiles));
1879
- break;
1880
- }
1881
- if (mime.startsWith("image/") && !canSeeImages) {
1882
- rejected.push(t.cantSeeImage(file.name));
1883
- continue;
1884
- }
1885
- if (!mime.startsWith("image/") && !isText(mime) && !upload) {
1886
- rejected.push(t.cantUpload(file.name));
1887
- continue;
1888
- }
1889
- const url = URL.createObjectURL(file);
1890
- urls.current.add(url);
1891
- next.push({ id: ++nextId.current, file, url });
1892
- }
1893
- return next;
1894
- });
1895
- setNotice(rejected.length ? rejected.join(" \xB7 ") : null);
1896
- ref.current?.focus({ preventScroll: true });
1897
- },
1898
- [busy, canSeeImages, upload, maxFiles, maxFileMb, t]
1899
- );
1900
- useEffect3(() => {
1901
- if (!withFiles) return;
1902
- let inside = 0;
1903
- const hasFiles = (e) => Array.from(e.dataTransfer?.types ?? []).includes("Files");
1904
- const enter = (e) => {
1905
- if (!hasFiles(e)) return;
1906
- e.preventDefault();
1907
- inside++;
1908
- if (!busy) setDragging(true);
1909
- };
1910
- const over = (e) => {
1911
- if (hasFiles(e)) e.preventDefault();
1912
- };
1913
- const leave = (e) => {
1914
- if (!hasFiles(e)) return;
1915
- inside = Math.max(0, inside - 1);
1916
- if (inside === 0) setDragging(false);
1917
- };
1918
- const drop = (e) => {
1919
- if (!hasFiles(e)) return;
1920
- e.preventDefault();
1921
- inside = 0;
1922
- setDragging(false);
1923
- if (e.dataTransfer?.files.length) addFiles(e.dataTransfer.files);
1924
- };
1925
- window.addEventListener("dragenter", enter);
1926
- window.addEventListener("dragover", over);
1927
- window.addEventListener("dragleave", leave);
1928
- window.addEventListener("drop", drop);
1929
- return () => {
1930
- window.removeEventListener("dragenter", enter);
1931
- window.removeEventListener("dragover", over);
1932
- window.removeEventListener("dragleave", leave);
1933
- window.removeEventListener("drop", drop);
1934
- };
1935
- }, [withFiles, busy, addFiles]);
1936
- const removeFile = (id) => setFiles((prev) => {
1937
- const out = prev.find((a) => a.id === id);
1938
- if (out) releaseUrl(out.url);
1939
- return prev.filter((a) => a.id !== id);
1940
- });
1941
- const submit = async () => {
1942
- const body = value.trim();
1943
- if (!body && files.length === 0 && references.length === 0) return;
1944
- if (busy && (files.length > 0 || !canSteer)) return;
1945
- if (uploading) return;
1946
- let atts = [];
1947
- if (files.length) setUploading(true);
1948
- try {
1949
- atts = await Promise.all(
1950
- files.map(async (a) => {
1951
- const mime = mimeOf(a.file);
1952
- if (mime.startsWith("image/") || isText(mime)) return readInline(a.file);
1953
- const up = await upload(a.file);
1954
- return { name: a.file.name, mimeType: mime, url: up.url };
1955
- })
1956
- );
1957
- } catch (e) {
1958
- setNotice(e instanceof Error ? e.message : t.uploadFailed);
1959
- return;
1960
- } finally {
1961
- setUploading(false);
1962
- }
1963
- onSubmit(body, atts);
1964
- setValue("");
1965
- setNotice(null);
1966
- for (const a of files) releaseUrl(a.url);
1967
- setFiles([]);
1968
- };
1969
- const [recording, setRecording] = useState6(false);
1970
- const [transcribing, setTranscribing] = useState6(false);
1971
- const [wave, setWave] = useState6([]);
1972
- const [seconds, setSeconds] = useState6(0);
1973
- const recRef = useRef3(null);
1974
- const pieces = useRef3([]);
1975
- const cancelRef = useRef3(false);
1976
- const meter = useRef3(null);
1977
- const [hasMic, setHasMic] = useState6(false);
1978
- useEffect3(() => setHasMic(typeof window !== "undefined" && "MediaRecorder" in window && Boolean(navigator.mediaDevices?.getUserMedia)), []);
1979
- const canRecord = Boolean(transcribe) && hasMic;
1980
- const toggleRecording = async () => {
1981
- if (recording) {
1982
- cancelRef.current = false;
1983
- recRef.current?.stop();
1984
- return;
1985
- }
1986
- try {
1987
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
1988
- const mime = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"].find((m) => MediaRecorder.isTypeSupported(m)) ?? "";
1989
- const rec = new MediaRecorder(stream, mime ? { mimeType: mime } : void 0);
1990
- pieces.current = [];
1991
- rec.ondataavailable = (e) => {
1992
- if (e.data.size > 0) pieces.current.push(e.data);
1993
- };
1994
- const ctx = new AudioContext();
1995
- const an = ctx.createAnalyser();
1996
- an.fftSize = 512;
1997
- ctx.createMediaStreamSource(stream).connect(an);
1998
- const buf = new Uint8Array(an.fftSize);
1999
- let lastTick = 0;
2000
- const tick = (time) => {
2001
- if (time - lastTick > 80) {
2002
- lastTick = time;
2003
- an.getByteTimeDomainData(buf);
2004
- let acc = 0;
2005
- for (let i = 0; i < buf.length; i++) {
2006
- const v = (buf[i] - 128) / 128;
2007
- acc += v * v;
2008
- }
2009
- setWave((w) => [...w.slice(-59), Math.min(1, Math.sqrt(acc / buf.length) * 4)]);
2010
- }
2011
- if (meter.current) meter.current.raf = requestAnimationFrame(tick);
2012
- };
2013
- const start = Date.now();
2014
- meter.current = { ctx, raf: requestAnimationFrame(tick), timer: window.setInterval(() => setSeconds(Math.floor((Date.now() - start) / 1e3)), 250) };
2015
- rec.onstop = async () => {
2016
- stream.getTracks().forEach((tr) => tr.stop());
2017
- if (meter.current) {
2018
- cancelAnimationFrame(meter.current.raf);
2019
- clearInterval(meter.current.timer);
2020
- void meter.current.ctx.close();
2021
- meter.current = null;
2022
- }
2023
- setRecording(false);
2024
- setWave([]);
2025
- setSeconds(0);
2026
- if (cancelRef.current) return;
2027
- const type = rec.mimeType || mime || "audio/webm";
2028
- const blob = new Blob(pieces.current, { type });
2029
- if (blob.size < 1e3) return;
2030
- setTranscribing(true);
2031
- try {
2032
- const ext = type.includes("mp4") ? "m4a" : "webm";
2033
- const name = `nota-de-voz-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.${ext}`;
2034
- const [said, up] = await Promise.all([transcribe(blob), upload ? upload(new File([blob], name, { type })) : Promise.resolve(null)]);
2035
- const text = said.trim();
2036
- if (!text) throw new Error(t.voiceEmpty);
2037
- const atts = up ? [{ name, mimeType: type, url: up.url }] : [];
2038
- onSubmit(voiceNoteText ? voiceNoteText(text) : text, atts);
2039
- setNotice(null);
2040
- } catch (e) {
2041
- setNotice(e instanceof Error ? e.message : t.voiceFailed);
2042
- } finally {
2043
- setTranscribing(false);
2044
- }
2045
- };
2046
- recRef.current = rec;
2047
- rec.start();
2048
- setRecording(true);
2049
- } catch {
2050
- setNotice(t.micDenied);
2051
- }
2052
- };
2053
- const canSend = Boolean(value.trim() || files.length > 0 || references.length > 0);
2054
- return /* @__PURE__ */ jsx8("div", { className: "gc-input rounded-(--piloto-radius-input) border-(length:--piloto-border-width) border-outlines bg-(--piloto-composer-bg) shadow-(--piloto-shadow-input) transition-colors focus-within:border-brand-300", children: /* @__PURE__ */ jsxs6("div", { className: `flex flex-col gap-2 p-3 transition-shadow ${withFiles && dragging ? "rounded-2xl bg-background-secondary/40 ring-2 ring-inset ring-ring-info" : ""}`, children: [
2055
- withFiles && dragging && /* @__PURE__ */ jsx8("div", { className: "pointer-events-none fixed inset-0 z-50 flex items-center justify-center bg-background-primary/80 p-6 backdrop-blur-sm", children: /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center gap-2 rounded-3xl border-2 border-dashed border-brand-500 bg-background-primary px-10 py-8 text-center shadow-lg", children: [
2056
- /* @__PURE__ */ jsx8(Paperclip, { className: "size-7 text-brand-500" }),
2057
- /* @__PURE__ */ jsx8("p", { className: "text-base font-semibold text-text-primary", children: t.dropFiles }),
2058
- /* @__PURE__ */ jsx8("p", { className: "text-xs text-text-secondary", children: t.dropHint })
2059
- ] }) }),
2060
- /* @__PURE__ */ jsx8(
2061
- "input",
2062
- {
2063
- ref: fileRef,
2064
- type: "file",
2065
- multiple: true,
2066
- hidden: true,
2067
- onChange: (e) => {
2068
- if (e.target.files) addFiles(e.target.files);
2069
- e.target.value = "";
2070
- }
2071
- }
2072
- ),
2073
- notice && /* @__PURE__ */ jsx8("p", { className: "text-xs text-text-danger", children: notice }),
2074
- uploading && /* @__PURE__ */ jsxs6("p", { className: "flex items-center gap-1.5 text-xs text-text-secondary", children: [
2075
- /* @__PURE__ */ jsx8(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" }),
2076
- files.length > 1 ? t.uploadingN(files.length) : t.uploading
2077
- ] }),
2078
- withFiles && files.length > 0 && /* @__PURE__ */ jsx8("div", { className: "flex flex-wrap gap-2", children: files.map((a) => /* @__PURE__ */ jsxs6("div", { className: "relative h-16 w-16 overflow-hidden rounded-lg border border-border-primary bg-background-secondary", children: [
2079
- mimeOf(a.file).startsWith("image/") ? /* @__PURE__ */ jsx8("img", { src: a.url, alt: a.file.name, className: "h-full w-full object-cover" }) : (
2080
- // Un PDF pintado con <img> es un recuadro roto: nombre, extensión y peso.
2081
- /* @__PURE__ */ jsxs6("span", { className: "flex h-full w-full flex-col items-center justify-center gap-1 px-1 text-center", title: a.file.name, children: [
2082
- /* @__PURE__ */ jsx8(FileText, { className: "h-4 w-4 shrink-0 text-text-tertiary" }),
2083
- /* @__PURE__ */ jsx8("span", { className: "w-full truncate text-[9px] font-medium uppercase leading-none text-text-secondary", children: a.file.name.match(/\.([A-Za-z0-9]+)$/)?.[1] ?? "file" }),
2084
- /* @__PURE__ */ jsx8("span", { className: "w-full truncate text-[9px] leading-none text-text-tertiary", children: shortSize(a.file.size) })
2085
- ] })
2086
- ),
2087
- /* @__PURE__ */ jsx8(
2088
- "button",
2089
- {
2090
- type: "button",
2091
- onClick: () => removeFile(a.id),
2092
- "aria-label": t.removeAttachment,
2093
- className: "absolute top-0.5 right-0.5 flex h-5 w-5 items-center justify-center rounded-full bg-background-inverse/80 text-text-inverse shadow-sm transition-colors hover:bg-background-inverse",
2094
- children: /* @__PURE__ */ jsx8(X, { className: "h-3 w-3" })
2095
- }
2096
- )
2097
- ] }, a.id)) }),
2098
- /* @__PURE__ */ jsx8(ReferenceChips, { references, onRemove: (id) => onRemoveReference?.(id), onActivate: onActivateReference }),
2099
- recording ? (
2100
- // Grabando: la barra sustituye al campo de texto — punto rojo, reloj, la onda y las dos salidas.
2101
- /* @__PURE__ */ jsxs6("div", { className: "flex min-h-[24px] items-center gap-3 px-1", children: [
2102
- /* @__PURE__ */ jsx8(
2103
- "button",
2104
- {
2105
- type: "button",
2106
- onClick: () => {
2107
- cancelRef.current = true;
2108
- recRef.current?.stop();
2109
- },
2110
- "aria-label": t.cancelVoice,
2111
- title: t.cancelVoice,
2112
- className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-text-secondary hover:bg-background-secondary hover:text-text-primary",
2113
- children: /* @__PURE__ */ jsx8(X, { className: "h-4 w-4" })
2114
- }
2115
- ),
2116
- /* @__PURE__ */ jsx8("span", { className: "h-2 w-2 shrink-0 animate-pulse rounded-full bg-red-500" }),
2117
- /* @__PURE__ */ jsxs6("span", { className: "w-10 shrink-0 font-mono text-xs tabular-nums text-text-secondary", children: [
2118
- Math.floor(seconds / 60),
2119
- ":",
2120
- String(seconds % 60).padStart(2, "0")
2121
- ] }),
2122
- /* @__PURE__ */ jsx8("div", { className: "flex h-6 min-w-0 flex-1 items-center gap-[2px] overflow-hidden", children: Array.from({ length: 60 }, (_, i) => wave[wave.length - 60 + i] ?? 0).map((v, i) => /* @__PURE__ */ jsx8("span", { className: "w-[3px] shrink-0 rounded-full bg-text-secondary/70", style: { height: `${Math.max(3, Math.round(v * 22))}px` } }, i)) }),
2123
- /* @__PURE__ */ jsx8(
2124
- "button",
2125
- {
2126
- type: "button",
2127
- onClick: toggleRecording,
2128
- "aria-label": t.stopAndSendVoice,
2129
- title: t.send,
2130
- className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-brand-500 text-white hover:bg-brand-600",
2131
- children: /* @__PURE__ */ jsx8(ArrowUp, { className: "h-4 w-4" })
2132
- }
2133
- )
2134
- ] })
2135
- ) : /* @__PURE__ */ jsx8(
2136
- "textarea",
2137
- {
2138
- ref,
2139
- rows: 1,
2140
- value,
2141
- placeholder,
2142
- onChange: (e) => setValue(e.target.value),
2143
- onKeyDown: (e) => {
2144
- if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
2145
- e.preventDefault();
2146
- void submit();
2147
- } else if (e.key === "Backspace" && !value && references.length) {
2148
- e.preventDefault();
2149
- onRemoveReference?.(references[references.length - 1].id);
2150
- }
2151
- },
2152
- className: "max-h-60 min-h-[24px] w-full flex-none resize-none overflow-y-auto bg-transparent px-1 text-sm leading-6 text-text-primary outline-none placeholder:text-text-tertiary"
2153
- }
2154
- ),
2155
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between gap-2", children: [
2156
- /* @__PURE__ */ jsxs6("div", { className: "@container flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden", children: [
2157
- withFiles && /* @__PURE__ */ jsxs6(Fragment5, { children: [
2158
- /* @__PURE__ */ jsx8(
2159
- "button",
2160
- {
2161
- type: "button",
2162
- onClick: () => {
2163
- if (!fileRef.current) return;
2164
- fileRef.current.accept = "image/*";
2165
- fileRef.current.click();
2166
- },
2167
- disabled: busy || !canSeeImages || files.length >= maxFiles,
2168
- "aria-label": canSeeImages ? t.attachImage : t.noImages,
2169
- title: canSeeImages ? t.attachImage : t.noImages,
2170
- className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-text-secondary transition-colors hover:bg-background-secondary hover:text-text-primary disabled:opacity-30",
2171
- children: /* @__PURE__ */ jsx8(Image, { className: "h-4 w-4" })
2172
- }
2173
- ),
2174
- extras,
2175
- /* @__PURE__ */ jsx8(
2176
- "button",
2177
- {
2178
- type: "button",
2179
- onClick: () => {
2180
- if (!fileRef.current) return;
2181
- fileRef.current.accept = "";
2182
- fileRef.current.click();
2183
- },
2184
- disabled: busy || files.length >= maxFiles,
2185
- "aria-label": t.attachFile,
2186
- title: t.attachFile,
2187
- className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-text-secondary transition-colors hover:bg-background-secondary hover:text-text-primary disabled:opacity-40",
2188
- children: /* @__PURE__ */ jsx8(Paperclip, { className: "h-4 w-4" })
2189
- }
2190
- )
2191
- ] }),
2192
- !withFiles && extras,
2193
- models.length > 0 && onModelChange && /* @__PURE__ */ jsx8(ModelPicker, { models, current: currentModel, onChange: onModelChange })
2194
- ] }),
2195
- /* @__PURE__ */ jsxs6("div", { className: "flex shrink-0 items-center gap-1.5", children: [
2196
- canRecord && !recording && /* @__PURE__ */ jsx8(
2197
- "button",
2198
- {
2199
- type: "button",
2200
- onClick: toggleRecording,
2201
- disabled: busy || transcribing,
2202
- "aria-label": t.recordVoice,
2203
- title: t.voiceNote,
2204
- className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-text-secondary transition-colors hover:bg-background-secondary hover:text-text-primary disabled:opacity-40",
2205
- children: transcribing ? /* @__PURE__ */ jsx8("span", { className: "h-3.5 w-3.5 animate-spin rounded-full border-2 border-current border-t-transparent" }) : /* @__PURE__ */ jsx8(Mic, { className: "h-4 w-4" })
2206
- }
2207
- ),
2208
- /* @__PURE__ */ jsx8(
2209
- "button",
2210
- {
2211
- type: "button",
2212
- onClick: busy ? onStop : () => void submit(),
2213
- disabled: !busy && !canSend || uploading,
2214
- "aria-label": busy ? t.stop : uploading ? t.uploading : t.send,
2215
- className: `gc-send flex h-8 w-8 items-center justify-center rounded-full transition-colors ${busy || canSend || uploading ? "bg-brand-500 text-white hover:bg-brand-600" : "bg-background-disabled text-text-disabled"}`,
2216
- children: busy ? /* @__PURE__ */ jsx8(Square, { className: "h-3 w-3 fill-current" }) : uploading ? /* @__PURE__ */ jsx8(LoaderCircle, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx8(ArrowUp, { className: "h-4 w-4" })
2217
- }
2218
- )
2219
- ] })
2220
- ] })
2221
- ] }) });
2222
- }
2223
- function ModelPicker({ models, current, onChange }) {
2224
- const { strings: t } = usePiloto();
2225
- const [open, setOpen] = useState6(false);
2226
- const box = useRef3(null);
2227
- useEffect3(() => {
2228
- if (!open) return;
2229
- const outside = (e) => {
2230
- if (!box.current?.contains(e.target)) setOpen(false);
2231
- };
2232
- const escape = (e) => e.key === "Escape" && setOpen(false);
2233
- document.addEventListener("mousedown", outside);
2234
- document.addEventListener("keydown", escape);
2235
- return () => {
2236
- document.removeEventListener("mousedown", outside);
2237
- document.removeEventListener("keydown", escape);
2238
- };
2239
- }, [open]);
2240
- if (models.length === 0) return null;
2241
- const active = models.find((m) => m.value === current) ?? null;
2242
- return /* @__PURE__ */ jsxs6("div", { ref: box, className: "relative", children: [
2243
- /* @__PURE__ */ jsxs6(
2244
- "button",
2245
- {
2246
- type: "button",
2247
- "aria-expanded": open,
2248
- onClick: () => setOpen((v) => !v),
2249
- className: "flex max-w-56 items-center gap-1.5 rounded-full border border-border-primary px-2.5 py-1 text-xs text-text-secondary transition-colors outline-none hover:bg-background-secondary hover:text-text-primary",
2250
- children: [
2251
- /* @__PURE__ */ jsx8(Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
2252
- /* @__PURE__ */ jsx8("span", { className: "truncate", children: active?.name ?? t.model }),
2253
- /* @__PURE__ */ jsx8(ChevronDown, { className: "h-3.5 w-3.5 shrink-0 opacity-70" })
2254
- ]
2255
- }
2256
- ),
2257
- open && // Hacia ARRIBA: el selector vive pegado al borde inferior del input.
2258
- /* @__PURE__ */ jsx8("div", { className: "absolute bottom-full left-0 z-20 mb-1 max-h-80 min-w-52 overflow-y-auto rounded-xl border border-border-primary bg-background-primary py-1 shadow-lg", children: models.map((m) => /* @__PURE__ */ jsxs6(
2259
- "button",
2260
- {
2261
- type: "button",
2262
- onClick: () => {
2263
- onChange(m.value);
2264
- setOpen(false);
2265
- },
2266
- className: "flex w-full items-center justify-between gap-3 px-3 py-1.5 text-left text-xs text-text-secondary transition-colors hover:bg-background-secondary hover:text-text-primary",
2267
- children: [
2268
- /* @__PURE__ */ jsx8("span", { className: "truncate", children: m.name }),
2269
- m.value === current && /* @__PURE__ */ jsx8(Check, { className: "h-3.5 w-3.5 shrink-0 text-text-success" })
2270
- ]
2271
- },
2272
- m.value
2273
- )) })
2274
- ] });
2275
- }
2276
-
2277
820
  // src/react/Status.tsx
2278
821
  import { AnimatePresence as AnimatePresence2, motion as motion2 } from "motion/react";
2279
- import { useEffect as useEffect4, useState as useState7 } from "react";
2280
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
822
+ import { useEffect as useEffect3, useState as useState6 } from "react";
823
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2281
824
  var STILL_WORKING_S = 90;
2282
825
  function TypingDots() {
2283
- return /* @__PURE__ */ jsx9("div", { className: "gc-typing flex gap-1", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx9("span", { className: "gc-typing-dot h-2 w-2 animate-bounce rounded-full", style: { "--i": i } }, i)) });
826
+ return /* @__PURE__ */ jsx7("div", { className: "gc-typing flex gap-1", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx7("span", { className: "gc-typing-dot h-2 w-2 animate-bounce rounded-full", style: { "--i": i } }, i)) });
2284
827
  }
2285
828
  function WaitLabel({
2286
829
  since,
@@ -2288,9 +831,9 @@ function WaitLabel({
2288
831
  step
2289
832
  }) {
2290
833
  const { strings: t } = usePiloto();
2291
- const [mounted] = useState7(() => Date.now());
2292
- const [now, setNow] = useState7(() => Date.now());
2293
- useEffect4(() => {
834
+ const [mounted] = useState6(() => Date.now());
835
+ const [now, setNow] = useState6(() => Date.now());
836
+ useEffect3(() => {
2294
837
  const id = setInterval(() => setNow(Date.now()), 1e3);
2295
838
  return () => clearInterval(id);
2296
839
  }, []);
@@ -2298,8 +841,8 @@ function WaitLabel({
2298
841
  const phrases = t.thinkingPhrases;
2299
842
  const stepText = step ? t.steps?.[step] : void 0;
2300
843
  const what = stepText ? stepText : phrases?.length ? seconds >= STILL_WORKING_S ? t.stillWorking : phrases[Math.floor(seconds / 4) % phrases.length] : t.howItGoes(seconds);
2301
- return /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-0.5 text-sm text-text-secondary", children: /* @__PURE__ */ jsxs7("p", { className: "flex items-center", children: [
2302
- /* @__PURE__ */ jsx9(AnimatePresence2, { mode: "wait", initial: false, children: /* @__PURE__ */ jsx9(
844
+ return /* @__PURE__ */ jsx7("div", { className: "flex flex-col gap-0.5 text-sm text-text-secondary", children: /* @__PURE__ */ jsxs5("p", { className: "flex items-center", children: [
845
+ /* @__PURE__ */ jsx7(AnimatePresence2, { mode: "wait", initial: false, children: /* @__PURE__ */ jsx7(
2303
846
  motion2.span,
2304
847
  {
2305
848
  className: "gc-wait-text",
@@ -2311,7 +854,7 @@ function WaitLabel({
2311
854
  },
2312
855
  what
2313
856
  ) }),
2314
- showDuration && /* @__PURE__ */ jsxs7("span", { className: "tabular-nums text-text-tertiary", children: [
857
+ showDuration && /* @__PURE__ */ jsxs5("span", { className: "tabular-nums text-text-tertiary", children: [
2315
858
  "\xA0\xB7 ",
2316
859
  formatDuration(seconds)
2317
860
  ] })
@@ -2319,7 +862,7 @@ function WaitLabel({
2319
862
  }
2320
863
 
2321
864
  // src/react/usePilot.ts
2322
- import { useCallback as useCallback2, useEffect as useEffect5, useMemo as useMemo3, useRef as useRef4, useState as useState8 } from "react";
865
+ import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef3, useState as useState7 } from "react";
2323
866
  var MAX_QUEUE_TRIES = 3;
2324
867
  var uuid = () => {
2325
868
  const c = globalThis.crypto;
@@ -2353,19 +896,29 @@ function fromUserMessage(content) {
2353
896
  return { text, attachments };
2354
897
  }
2355
898
  function usePilot(transport, opts = {}) {
2356
- const [state, setState] = useState8(() => createPilotState(opts.initialTurns ?? []));
2357
- const ref = useRef4(state);
2358
- const optsRef = useRef4(opts);
899
+ const [state, setState] = useState7(() => createPilotState(opts.initialTurns ?? []));
900
+ const ref = useRef3(state);
901
+ const optsRef = useRef3(opts);
2359
902
  optsRef.current = opts;
2360
- const transportRef = useRef4(transport);
903
+ const transportRef = useRef3(transport);
2361
904
  transportRef.current = transport;
2362
- const [threadId] = useState8(() => opts.threadId ?? uuid());
2363
- const ctrl = useRef4(null);
2364
- const liveRun = useRef4(null);
2365
- const answers = useRef4(/* @__PURE__ */ new Map());
2366
- const queuedTries = useRef4(/* @__PURE__ */ new Map());
2367
- const flushRef = useRef4(() => {
905
+ const [threadId] = useState7(() => opts.threadId ?? uuid());
906
+ const ctrl = useRef3(null);
907
+ const liveRun = useRef3(null);
908
+ const answers = useRef3(/* @__PURE__ */ new Map());
909
+ const queuedTries = useRef3(/* @__PURE__ */ new Map());
910
+ const flushRef = useRef3(() => {
2368
911
  });
912
+ const turnListeners = useRef3(/* @__PURE__ */ new Set());
913
+ const onTurn = useCallback((fn) => {
914
+ turnListeners.current.add(fn);
915
+ return () => void turnListeners.current.delete(fn);
916
+ }, []);
917
+ const listeners = useRef3(/* @__PURE__ */ new Set());
918
+ const subscribe = useCallback((fn) => {
919
+ listeners.current.add(fn);
920
+ return () => void listeners.current.delete(fn);
921
+ }, []);
2369
922
  const baseInput = () => {
2370
923
  const ctx = optsRef.current.context;
2371
924
  return { threadId, runId: uuid(), messages: [], tools: [], context: toAgUiContext(typeof ctx === "function" ? ctx() : ctx), state: {}, forwardedProps: extraProps() };
@@ -2374,19 +927,24 @@ function usePilot(transport, opts = {}) {
2374
927
  const f = optsRef.current.forwardedProps;
2375
928
  return (typeof f === "function" ? f() : f) ?? {};
2376
929
  };
2377
- const commit = useCallback2((next) => {
930
+ const commit = useCallback((next) => {
2378
931
  if (next === ref.current) return;
2379
932
  ref.current = next;
2380
933
  setState(next);
2381
934
  const o = optsRef.current;
2382
935
  for (const e of next.effects) {
2383
- if (e.type === "turn-start") o.onTurnStart?.();
2384
- else if (e.type === "turn-done") o.onTurnDone?.({ text: e.text, runId: e.runId, seconds: e.seconds, error: e.error, interrupted: e.interrupted });
2385
- else if (e.type === "title") o.onTitle?.(e.title);
936
+ if (e.type === "turn-start") {
937
+ o.onTurnStart?.();
938
+ for (const fn of turnListeners.current) fn({ type: "start" });
939
+ } else if (e.type === "turn-done") {
940
+ const info = { text: e.text, runId: e.runId, seconds: e.seconds, error: e.error, interrupted: e.interrupted };
941
+ o.onTurnDone?.(info);
942
+ for (const fn of turnListeners.current) fn({ type: "done", info });
943
+ } else if (e.type === "title") o.onTitle?.(e.title);
2386
944
  else if (e.type === "state") o.onState?.(e.state);
2387
945
  }
2388
946
  }, []);
2389
- const runNow = useCallback2(
947
+ const runNow = useCallback(
2390
948
  async (partial, reopening = false) => {
2391
949
  const t = transportRef.current;
2392
950
  if (!t) return;
@@ -2405,6 +963,7 @@ function usePilot(transport, opts = {}) {
2405
963
  (e) => {
2406
964
  if (c.signal.aborted) return;
2407
965
  optsRef.current.onEvent?.(e);
966
+ for (const fn of listeners.current) fn(e);
2408
967
  if (e.type === "RUN_FINISHED" || e.type === "RUN_ERROR") {
2409
968
  ended = true;
2410
969
  liveRun.current = null;
@@ -2452,7 +1011,8 @@ function usePilot(transport, opts = {}) {
2452
1011
  commit({ ...ref.current, notice: err instanceof Error && err.message ? err.message : null, effects: [] });
2453
1012
  return runNow({ messages: [] }, true);
2454
1013
  }
2455
- commit(sendFailed(ref.current, err instanceof Error && err.message ? err.message : "No se pudo hablar con el agente."));
1014
+ const failed = sendFailed(ref.current, err instanceof Error && err.message ? err.message : "No se pudo hablar con el agente.");
1015
+ commit(err instanceof PilotHttpError ? { ...failed, httpError: { status: err.status, ...err.code ? { code: err.code } : {}, message: err.message, ...err.headers ? { headers: err.headers } : {} } } : failed);
2456
1016
  return;
2457
1017
  }
2458
1018
  if (c.signal.aborted) return;
@@ -2465,17 +1025,31 @@ function usePilot(transport, opts = {}) {
2465
1025
  },
2466
1026
  [commit, threadId]
2467
1027
  );
2468
- useEffect5(() => {
2469
- if (!transport || !opts.threadId) return;
2470
- void runNow({ messages: [] }, true);
2471
- return () => ctrl.current?.abort();
1028
+ const sentInitial = useRef3(false);
1029
+ useEffect4(() => {
1030
+ if (!transport) return;
1031
+ let alive = true;
1032
+ const sendInitial = () => {
1033
+ const prompt = optsRef.current.initialPrompt;
1034
+ if (!alive || !prompt || sentInitial.current) return;
1035
+ sentInitial.current = true;
1036
+ void sendRef.current(prompt);
1037
+ };
1038
+ if (opts.threadId) void runNow({ messages: [] }, true).then(sendInitial);
1039
+ else setTimeout(sendInitial, 0);
1040
+ return () => {
1041
+ alive = false;
1042
+ ctrl.current?.abort();
1043
+ };
2472
1044
  }, [transport]);
2473
1045
  const pendingResume = () => [
2474
1046
  ...ref.current.permissions.filter((p) => !answers.current.has(p.id)).map((p) => ({ interruptId: p.id, status: "cancelled" })),
2475
1047
  ...[...answers.current.entries()].map(([interruptId, a]) => ({ interruptId, ...a }))
2476
1048
  ];
1049
+ const sendRef = useRef3(async () => {
1050
+ });
2477
1051
  const steerOn = () => optsRef.current.steer ?? ref.current.agentState.caps?.steer === true;
2478
- const send = useCallback2(
1052
+ const send = useCallback(
2479
1053
  async (text, attachments = [], _extra = {}) => {
2480
1054
  if (!transportRef.current) return;
2481
1055
  if (ref.current.busy) {
@@ -2490,7 +1064,8 @@ function usePilot(transport, opts = {}) {
2490
1064
  // eslint-disable-next-line react-hooks/exhaustive-deps
2491
1065
  [commit, runNow]
2492
1066
  );
2493
- const sendNew = useCallback2(
1067
+ sendRef.current = (text) => send(text);
1068
+ const sendNew = useCallback(
2494
1069
  async (text, attachments, tries = 0) => {
2495
1070
  const resume = pendingResume();
2496
1071
  answers.current.clear();
@@ -2517,7 +1092,7 @@ function usePilot(transport, opts = {}) {
2517
1092
  const attachments = queued.flatMap((q) => q.attachments ?? []);
2518
1093
  void sendNew(text, attachments, Math.max(...queued.map((q) => q.tries)));
2519
1094
  };
2520
- const stop = useCallback2(() => {
1095
+ const stop = useCallback(() => {
2521
1096
  const run = liveRun.current ?? (ref.current.busy ? ref.current.runId : null);
2522
1097
  liveRun.current = null;
2523
1098
  ctrl.current?.abort();
@@ -2530,14 +1105,14 @@ function usePilot(transport, opts = {}) {
2530
1105
  }).catch(() => {
2531
1106
  });
2532
1107
  }, [commit, threadId]);
2533
- const retry = useCallback2(() => {
1108
+ const retry = useCallback(() => {
2534
1109
  const s = ref.current;
2535
1110
  const last = lastUserTurn(s.turns);
2536
1111
  if (!last || s.busy) return;
2537
1112
  commit(dropLastExchange(s));
2538
1113
  void send(last.text, last.attachments ?? []);
2539
1114
  }, [commit, send]);
2540
- const sendAnswersIfComplete = useCallback2(() => {
1115
+ const sendAnswersIfComplete = useCallback(() => {
2541
1116
  const open = ref.current.permissions.filter((p) => !answers.current.has(p.id));
2542
1117
  if (open.length || !answers.current.size || ref.current.busy) return;
2543
1118
  const resume = [...answers.current.entries()].map(([interruptId, a]) => ({ interruptId, ...a }));
@@ -2545,18 +1120,18 @@ function usePilot(transport, opts = {}) {
2545
1120
  commit(beginSend(ref.current, "", []));
2546
1121
  void runNow({ messages: [], resume });
2547
1122
  }, [commit, runNow]);
2548
- const respondPermission = useCallback2(
1123
+ const respondPermission = useCallback(
2549
1124
  (id, approved) => {
2550
1125
  answers.current.set(id, { status: "resolved", payload: { approved } });
2551
1126
  sendAnswersIfComplete();
2552
1127
  },
2553
1128
  [sendAnswersIfComplete]
2554
1129
  );
2555
- const recordDecision = useCallback2(
1130
+ const recordDecision = useCallback(
2556
1131
  (id, status, asked) => commit(resolvePermission(ref.current, id, status, asked)),
2557
1132
  [commit]
2558
1133
  );
2559
- const expirePermission = useCallback2(
1134
+ const expirePermission = useCallback(
2560
1135
  (id) => {
2561
1136
  if (!ref.current.permissions.some((p) => p.id === id)) return;
2562
1137
  answers.current.set(id, { status: "cancelled" });
@@ -2565,9 +1140,9 @@ function usePilot(transport, opts = {}) {
2565
1140
  },
2566
1141
  [commit, sendAnswersIfComplete]
2567
1142
  );
2568
- const [loadingOlder, setLoadingOlder] = useState8(false);
2569
- const loadingRef = useRef4(false);
2570
- const loadOlder = useCallback2(async () => {
1143
+ const [loadingOlder, setLoadingOlder] = useState7(false);
1144
+ const loadingRef = useRef3(false);
1145
+ const loadOlder = useCallback(async () => {
2571
1146
  const fn = optsRef.current.loadOlder;
2572
1147
  const h = ref.current.history;
2573
1148
  if (!fn || !h?.hasMoreBefore || loadingRef.current) return;
@@ -2584,7 +1159,7 @@ function usePilot(transport, opts = {}) {
2584
1159
  setLoadingOlder(false);
2585
1160
  }
2586
1161
  }, [commit]);
2587
- const stopSubagent = useCallback2(
1162
+ const stopSubagent = useCallback(
2588
1163
  async (taskId) => {
2589
1164
  const t = transportRef.current;
2590
1165
  if (!t) return;
@@ -2598,96 +1173,186 @@ function usePilot(transport, opts = {}) {
2598
1173
  // eslint-disable-next-line react-hooks/exhaustive-deps
2599
1174
  [commit, runNow]
2600
1175
  );
2601
- const continueRun = useCallback2(async () => {
1176
+ const continueRun = useCallback(async () => {
2602
1177
  const cut = ref.current.cutRun;
2603
1178
  if (!cut || ref.current.busy || !transportRef.current) return;
2604
1179
  commit(beginSend(ref.current, "", []));
2605
1180
  await runNow({ messages: [], forwardedProps: { continueRun: cut.runId } });
2606
1181
  }, [commit, runNow]);
2607
- const refresh = useCallback2(() => runNow({ messages: [] }, true), [runNow]);
1182
+ const refresh = useCallback(() => runNow({ messages: [] }, true), [runNow]);
2608
1183
  const canSteer = Boolean(transport) && (opts.steer ?? state.agentState.caps?.steer === true);
2609
- const subagents = useMemo3(() => subagentsOf(state.agentState), [state.agentState]);
2610
- return { state, threadId, send, stop, retry, respondPermission, recordDecision, expirePermission, canSteer, loadOlder, loadingOlder, subagents, stopSubagent, refresh, continueRun };
1184
+ const subagents = useMemo2(() => subagentsOf(state.agentState), [state.agentState]);
1185
+ return { state, threadId, send, stop, retry, respondPermission, recordDecision, expirePermission, canSteer, loadOlder, loadingOlder, subagents, stopSubagent, refresh, reconnect: refresh, continueRun, subscribe, onTurn };
2611
1186
  }
2612
1187
 
2613
1188
  // src/react/Piloto.tsx
2614
- import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2615
- var useIsoLayoutEffect = typeof window === "undefined" ? useEffect6 : useLayoutEffect;
1189
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1190
+ var useIsoLayoutEffect = typeof window === "undefined" ? useEffect5 : useLayoutEffect;
2616
1191
  var ANCHOR_GAP = 24;
2617
- var Piloto = forwardRef3(function Piloto2(props, handle) {
2618
- const { transport, pilot: external, locale = "es", theme = "light", features = {}, slots = {} } = props;
1192
+ var Piloto = forwardRef(function Piloto2(props, handle) {
1193
+ const { transport, pilot: external, locale = "es", theme = "light", features = {} } = props;
2619
1194
  const own = usePilot(external ? null : transport, features.steer === void 0 ? props : { ...props, steer: features.steer });
2620
1195
  const pilot = external ?? own;
2621
1196
  const { state } = pilot;
2622
1197
  const { turns, busy, error, notice, permissions, olderCount } = state;
2623
- const strings = useMemo4(() => ({ ...locale === "en" ? STRINGS_EN : STRINGS_ES, ...props.strings }), [locale, props.strings]);
2624
- const config = useMemo4(
1198
+ const plugins = useMemo3(() => resolvePlugins(props.plugins), [props.plugins]);
1199
+ useEffect5(() => {
1200
+ if (isDev()) for (const w of plugins.warnings) warnOnce(w);
1201
+ }, [plugins]);
1202
+ const ctxForComponents = useRef4(null);
1203
+ const boundComponents = useMemo3(() => {
1204
+ const entries = Object.entries(plugins.components);
1205
+ if (!entries.length) return void 0;
1206
+ return Object.fromEntries(
1207
+ entries.map(([tag, { plugin, value: C }]) => [
1208
+ tag,
1209
+ // Como toda vista de plugin: marcada con su nombre y, si es de la app, aislada de las utilidades.
1210
+ (p) => /* @__PURE__ */ jsx8("span", { ...pluginAttrs(plugin, plugins.ghostykitStyled), style: { display: "contents" }, children: /* @__PURE__ */ jsx8(C, { ...p, ctx: ctxForComponents.current }) })
1211
+ ])
1212
+ );
1213
+ }, [plugins]);
1214
+ const strings = useMemo3(() => ({ ...locale === "en" ? STRINGS_EN : STRINGS_ES, ...plugins.strings }), [locale, plugins]);
1215
+ const config = useMemo3(
2625
1216
  () => ({
2626
1217
  strings,
2627
- toolVerbs: { ...locale === "en" ? TOOL_VERBS_EN : TOOL_VERBS_ES, ...props.toolVerbs },
2628
- toolIcons: props.toolIcons ?? {},
2629
- formatUserText: props.formatUserText ?? cleanUserText,
1218
+ toolVerbs: { ...locale === "en" ? TOOL_VERBS_EN : TOOL_VERBS_ES, ...plugins.verbs },
1219
+ toolIcons: plugins.icons,
1220
+ formatUserText: plugins.formatUserText ? (x) => plugins.formatUserText(cleanUserText(x)) : cleanUserText,
2630
1221
  theme,
2631
1222
  skin: props.skin ?? "ghosty",
2632
- markdown: props.markdown ?? {}
1223
+ markdown: { ...plugins.markdown, ...boundComponents ? { components: boundComponents } : {} }
2633
1224
  }),
2634
- [strings, locale, props.toolVerbs, props.toolIcons, props.formatUserText, theme, props.skin, props.markdown]
1225
+ [strings, locale, plugins, theme, props.skin, boundComponents]
2635
1226
  );
2636
1227
  const t = strings;
2637
- const [ownRefs, setOwnRefs] = useState9([]);
1228
+ const [ownRefs, setOwnRefs] = useState8([]);
2638
1229
  const refs = props.references ?? ownRefs;
2639
- const refsRef = useRef5(refs);
1230
+ const refsRef = useRef4(refs);
2640
1231
  refsRef.current = refs;
2641
- const setRefs = useCallback3(
1232
+ const setRefs = useCallback2(
2642
1233
  (next) => {
2643
1234
  if (!props.references) setOwnRefs(next);
2644
1235
  props.onReferencesChange?.(next);
2645
1236
  },
2646
1237
  [props.references, props.onReferencesChange]
2647
1238
  );
2648
- const composerBox = useRef5(null);
1239
+ const composerBox = useRef4(null);
2649
1240
  const draftText = typeof props.draft === "string" ? props.draft : props.draft?.text;
2650
1241
  const draftKey = typeof props.draft === "object" && props.draft ? props.draft.key : void 0;
2651
- const [draft, setDraftState] = useState9(() => ({ text: draftText ?? "", n: 0 }));
2652
- const draftSeen = useRef5(`${draftText}\0${draftKey}`);
2653
- useEffect6(() => {
1242
+ const [draft, setDraftState] = useState8(() => ({ text: draftText ?? "", n: 0 }));
1243
+ const draftSeen = useRef4(`${draftText}\0${draftKey}`);
1244
+ useEffect5(() => {
2654
1245
  const id = `${draftText}\0${draftKey}`;
2655
1246
  if (draftText == null || id === draftSeen.current) return;
2656
1247
  draftSeen.current = id;
2657
1248
  setDraftState((d) => ({ text: draftText, n: d.n + 1 }));
2658
1249
  }, [draftText, draftKey]);
2659
1250
  const returned = state.returnedDraft;
2660
- useEffect6(() => {
1251
+ useEffect5(() => {
2661
1252
  if (!returned) return;
2662
1253
  const typed = composerBox.current?.querySelector("textarea")?.value ?? "";
2663
1254
  const text = [returned.text, typed].filter((x) => x.trim()).join("\n\n");
2664
1255
  setDraftState((d) => ({ text, n: d.n + 1 }));
2665
1256
  }, [returned?.n]);
2666
- const send = useCallback3(
1257
+ const send = useCallback2(
2667
1258
  (text, attachments = []) => {
2668
1259
  const current = refsRef.current;
2669
- const full = applyReferences(text, current);
1260
+ const before = pluginsRef.current.beforeSend;
1261
+ const full = applyReferences(before && ctxRef.current ? before(text, ctxRef.current) : text, current);
2670
1262
  if (current.length) setRefs([]);
2671
1263
  void pilot.send(full, attachments, current.length ? { references: current } : {});
2672
1264
  },
2673
1265
  [pilot, setRefs]
2674
1266
  );
2675
- useImperativeHandle(
2676
- handle,
2677
- () => ({
2678
- send,
2679
- addReference: (r) => {
2680
- const current = refsRef.current;
2681
- if (!current.some((x) => x.id === r.id)) setRefs([...current, r]);
2682
- requestAnimationFrame(() => composerBox.current?.querySelector("textarea")?.focus({ preventScroll: true }));
2683
- },
2684
- setDraft: (text) => setDraftState((d) => ({ text, n: d.n + 1 })),
2685
- focus: () => composerBox.current?.querySelector("textarea")?.focus({ preventScroll: true })
1267
+ const pluginsRef = useRef4(plugins);
1268
+ pluginsRef.current = plugins;
1269
+ const ctxRef = useRef4(null);
1270
+ const composerHandle = useRef4(null);
1271
+ const focus = useCallback2(() => composerBox.current?.querySelector("textarea")?.focus({ preventScroll: true }), []);
1272
+ const addReference = useCallback2(
1273
+ (r) => {
1274
+ const current = refsRef.current;
1275
+ if (!current.some((x) => x.id === r.id)) setRefs([...current, r]);
1276
+ requestAnimationFrame(focus);
1277
+ },
1278
+ [setRefs, focus]
1279
+ );
1280
+ const setDraft = useCallback2((text) => setDraftState((d) => ({ text, n: d.n + 1 })), []);
1281
+ const addAttachments = useCallback2((items) => composerHandle.current?.addAttachments(items), []);
1282
+ const actions = { pilot, send, addReference, addAttachments, setDraft, focus };
1283
+ useImperativeHandle(handle, () => actions, [pilot, send, addReference, addAttachments, setDraft, focus]);
1284
+ const ctx = { ...actions, agentState: state.agentState, strings, plugins: plugins.list };
1285
+ ctxForComponents.current = ctx;
1286
+ const mark = (plugin) => pluginAttrs(plugin, plugins.ghostykitStyled);
1287
+ ctxRef.current = ctx;
1288
+ const unattended = useRef4(/* @__PURE__ */ new Set());
1289
+ useEffect5(
1290
+ () => pilot.subscribe?.((event) => {
1291
+ const e = event;
1292
+ const r = pluginsRef.current;
1293
+ const handlers = [...r.events[e.type] ?? [], ...e.type === "CUSTOM" && e.name ? r.events[e.name] ?? [] : [], ...r.events["*"] ?? []];
1294
+ for (const h of handlers) {
1295
+ try {
1296
+ h.value(e, ctxRef.current);
1297
+ } catch (err) {
1298
+ console.error(`[ghostykit] el plugin \xAB${h.plugin}\xBB fall\xF3 con ${e.type}${e.name ? ` ${e.name}` : ""}`, err);
1299
+ }
1300
+ }
1301
+ if (e.type === "CUSTOM" && e.name && isDev() && !BUILTIN_CUSTOM.has(e.name) && !r.events[e.name] && !r.customRenders[e.name] && !unattended.current.has(e.name)) {
1302
+ const declared = r.list.some((p) => p.requires?.events?.includes(e.name));
1303
+ if (!declared) {
1304
+ unattended.current.add(e.name);
1305
+ warnOnce(`Ning\xFAn plugin atiende CUSTOM \xAB${e.name}\xBB.`);
1306
+ }
1307
+ }
1308
+ }),
1309
+ [pilot.subscribe]
1310
+ );
1311
+ const seenState = useRef4({});
1312
+ useEffect5(() => {
1313
+ const r = pluginsRef.current;
1314
+ for (const [key, handlers] of Object.entries(r.state)) {
1315
+ const value = state.agentState[key];
1316
+ if (value === seenState.current[key]) continue;
1317
+ seenState.current[key] = value;
1318
+ if (value === void 0) continue;
1319
+ for (const h of handlers) {
1320
+ try {
1321
+ h.value(value, ctxRef.current);
1322
+ } catch (err) {
1323
+ console.error(`[ghostykit] el plugin \xAB${h.plugin}\xBB fall\xF3 con state.${key}`, err);
1324
+ }
1325
+ }
1326
+ }
1327
+ }, [state.agentState, plugins]);
1328
+ useEffect5(
1329
+ () => pilot.onTurn?.((e) => {
1330
+ const r = pluginsRef.current;
1331
+ const run = (plugin, fn) => {
1332
+ try {
1333
+ fn();
1334
+ } catch (err) {
1335
+ console.error(`[ghostykit] el plugin \xAB${plugin}\xBB fall\xF3 al ${e.type === "start" ? "empezar" : "terminar"} el turno`, err);
1336
+ }
1337
+ };
1338
+ if (e.type === "start") for (const h of r.turnStart) run(h.plugin, () => h.value(ctxRef.current));
1339
+ else for (const h of r.turnDone) run(h.plugin, () => h.value(e.info, ctxRef.current));
2686
1340
  }),
2687
- [send, setRefs]
1341
+ [pilot.onTurn]
2688
1342
  );
2689
- const [held, setHeld] = useState9({});
2690
- const heldRef = useRef5(held);
1343
+ useEffect5(() => {
1344
+ const err = state.httpError;
1345
+ if (!err) return;
1346
+ for (const h of pluginsRef.current.httpError) {
1347
+ try {
1348
+ h.value(err, ctxRef.current);
1349
+ } catch (e) {
1350
+ console.error(`[ghostykit] el plugin \xAB${h.plugin}\xBB fall\xF3 con onHttpError`, e);
1351
+ }
1352
+ }
1353
+ }, [state.httpError]);
1354
+ const [held, setHeld] = useState8({});
1355
+ const heldRef = useRef4(held);
2691
1356
  heldRef.current = held;
2692
1357
  const releaseHeld = (id) => setHeld(({ [id]: _, ...rest }) => rest);
2693
1358
  const respond = (id, approved) => {
@@ -2703,15 +1368,15 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2703
1368
  releaseHeld(id);
2704
1369
  };
2705
1370
  const cards = [...permissions, ...Object.values(held).filter((h) => !permissions.some((p) => p.id === h.id))];
2706
- const scroller = useRef5(null);
2707
- const column = useRef5(null);
2708
- const spacer = useRef5(null);
2709
- const following = useRef5(true);
2710
- const touchY = useRef5(null);
2711
- const [atBottom, setAtBottom] = useState9(true);
1371
+ const scroller = useRef4(null);
1372
+ const column = useRef4(null);
1373
+ const spacer = useRef4(null);
1374
+ const following = useRef4(true);
1375
+ const touchY = useRef4(null);
1376
+ const [atBottom, setAtBottom] = useState8(true);
2712
1377
  const message = (n) => scroller.current?.querySelector(`[data-turn="${n}"]`) ?? null;
2713
- const olderSeen = useRef5(olderCount);
2714
- const heightBefore = useRef5(0);
1378
+ const olderSeen = useRef4(olderCount);
1379
+ const heightBefore = useRef4(0);
2715
1380
  useIsoLayoutEffect(() => {
2716
1381
  const el = scroller.current;
2717
1382
  if (el && olderCount > olderSeen.current && heightBefore.current) el.scrollTop += el.scrollHeight - heightBefore.current;
@@ -2722,8 +1387,8 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2722
1387
  heightBefore.current = scroller.current?.scrollHeight ?? 0;
2723
1388
  void pilot.loadOlder();
2724
1389
  };
2725
- const placed = useRef5(false);
2726
- useEffect6(() => {
1390
+ const placed = useRef4(false);
1391
+ useEffect5(() => {
2727
1392
  if (placed.current || turns.length === 0) return;
2728
1393
  placed.current = true;
2729
1394
  const el = scroller.current;
@@ -2738,16 +1403,20 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2738
1403
  }
2739
1404
  }
2740
1405
  const mine = mineIndex >= 0 ? turns[mineIndex] : null;
2741
- const mineSeen = useRef5(mine);
2742
- const [anchor, setAnchor] = useState9(null);
2743
- useEffect6(() => {
1406
+ const mineSeen = useRef4(mine);
1407
+ const [anchor, setAnchor] = useState8(null);
1408
+ useEffect5(() => {
2744
1409
  if (mine === mineSeen.current) return;
2745
1410
  mineSeen.current = mine;
1411
+ if (!state.turnStartedAt) {
1412
+ following.current = true;
1413
+ return;
1414
+ }
2746
1415
  if (mine) setAnchor(mineIndex - olderCount);
2747
1416
  following.current = true;
2748
1417
  }, [mine, mineIndex]);
2749
1418
  const gapAbove = (a) => anchor === -olderCount ? a.offsetTop : ANCHOR_GAP;
2750
- const measureSpacer = useCallback3(() => {
1419
+ const measureSpacer = useCallback2(() => {
2751
1420
  const sc = scroller.current;
2752
1421
  const sp = spacer.current;
2753
1422
  if (!sc || !sp) return;
@@ -2765,7 +1434,7 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2765
1434
  const a = anchor != null ? message(anchor) : null;
2766
1435
  if (sc && a) sc.scrollTo({ top: a.offsetTop - gapAbove(a), behavior: "smooth" });
2767
1436
  }, [anchor, measureSpacer]);
2768
- useEffect6(() => {
1437
+ useEffect5(() => {
2769
1438
  const col = column.current;
2770
1439
  const sc = scroller.current;
2771
1440
  if (!col || !sc || typeof ResizeObserver === "undefined") return;
@@ -2795,13 +1464,22 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2795
1464
  const toolRunning = busy && last?.role === "assistant" && (last.tools ?? []).some(isRunning);
2796
1465
  const empty = turns.length === 0 && !busy;
2797
1466
  const placeholder = props.placeholder ?? (props.agentName ? t.placeholder(props.agentName) : t.placeholderDefault);
2798
- const showFiles = features.attachments ?? Boolean(props.upload);
1467
+ const { upload, transcribe, voiceNoteText, accept, maxFiles, maxBytes } = plugins.attachments;
1468
+ const showFiles = features.attachments ?? Boolean(upload);
2799
1469
  const pointedRefs = refs.filter((r) => r.prompt);
2800
1470
  const plainRefs = refs.filter((r) => !r.prompt);
2801
1471
  const removeRef = (id) => setRefs(refsRef.current.filter((r) => r.id !== id));
2802
- const composer = /* @__PURE__ */ jsxs8("div", { ref: composerBox, children: [
2803
- /* @__PURE__ */ jsx10(ReferenceChips, { references: pointedRefs, onRemove: removeRef, onActivate: props.onReferenceClick }),
2804
- /* @__PURE__ */ jsx10(
1472
+ const replacer = plugins.replacers.find((r) => {
1473
+ try {
1474
+ return r.value.when(ctx);
1475
+ } catch {
1476
+ return false;
1477
+ }
1478
+ });
1479
+ const Replacement = replacer?.value.view;
1480
+ const composer = Replacement ? /* @__PURE__ */ jsx8("div", { ...mark(replacer.plugin), children: /* @__PURE__ */ jsx8(Replacement, { ctx }) }) : /* @__PURE__ */ jsxs6("div", { ref: composerBox, children: [
1481
+ /* @__PURE__ */ jsx8(ReferenceChips, { references: pointedRefs, onRemove: removeRef, onActivate: props.onReferenceClick }),
1482
+ /* @__PURE__ */ jsx8(
2805
1483
  Composer,
2806
1484
  {
2807
1485
  initialValue: draft.text,
@@ -2811,156 +1489,236 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2811
1489
  canSteer: pilot.canSteer,
2812
1490
  placeholder,
2813
1491
  attachments: showFiles,
2814
- upload: props.upload,
2815
- transcribe: props.transcribe,
2816
- voiceNoteText: props.voiceNoteText,
1492
+ upload,
1493
+ transcribe,
1494
+ voiceNoteText,
2817
1495
  references: plainRefs,
2818
1496
  onRemoveReference: removeRef,
2819
1497
  onActivateReference: props.onReferenceClick,
2820
- extras: slots.composerExtras
1498
+ extras: plugins.composerActions.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.composerActions, ctx, styled: plugins.ghostykitStyled, inline: true }) : void 0,
1499
+ extrasEnd: plugins.composerActionsEnd.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.composerActionsEnd, ctx, styled: plugins.ghostykitStyled, inline: true }) : void 0,
1500
+ handleRef: composerHandle,
1501
+ accept,
1502
+ ...maxFiles ? { maxFiles } : {},
1503
+ ...maxBytes ? { maxFileMb: maxBytes / (1024 * 1024) } : {}
2821
1504
  },
2822
1505
  draft.n
2823
1506
  )
2824
1507
  ] });
2825
- const withPanel = (chat) => slots.sidePanel ? /* @__PURE__ */ jsxs8("div", { className: "flex min-h-0 flex-1 flex-row", children: [
2826
- /* @__PURE__ */ jsx10("div", { className: "relative flex min-h-0 min-w-0 flex-1 flex-col", children: chat }),
2827
- slots.sidePanel(state.agentState, pilot)
1508
+ const withPanel = (chat) => plugins.sidePanel.length ? /* @__PURE__ */ jsxs6("div", { className: "flex min-h-0 flex-1 flex-row", children: [
1509
+ /* @__PURE__ */ jsx8("div", { className: "relative flex min-h-0 min-w-0 flex-1 flex-col", children: chat }),
1510
+ /* @__PURE__ */ jsx8(Views, { items: plugins.sidePanel, ctx, styled: plugins.ghostykitStyled, className: "flex min-h-0 shrink-0" })
2828
1511
  ] }) : chat;
2829
- const aboveComposer = typeof slots.aboveComposer === "function" ? slots.aboveComposer(pilot) : slots.aboveComposer;
2830
- const permissionCards = cards.map((p) => /* @__PURE__ */ jsx10(ConfirmCard, { request: p, onRespond: respond, onSettled: settle, onExpire: pilot.expirePermission }, p.id));
2831
- return /* @__PURE__ */ jsx10(PilotoContext.Provider, { value: config, children: /* @__PURE__ */ jsx10("div", { ...rootProps(theme, props.skin), "data-busy": busy || void 0, className: `piloto ${props.className ?? ""}`, style: props.style, children: withPanel(empty ? (
2832
- // Un hilo vacío enseña lo mismo que la portada: la cara, el saludo, la caja y por dónde
2833
- // empezar. Si no cabe (panel bajo), se desplaza: `safe center` nunca empuja nada fuera.
2834
- /* @__PURE__ */ jsxs8("div", { className: "gs-scroll flex min-h-0 flex-1 flex-col items-center justify-center overflow-y-auto px-4 pb-[10vh] [justify-content:safe_center]", children: [
2835
- slots.headerActions && /* @__PURE__ */ jsx10("div", { className: "absolute right-4 top-3 flex gap-2", children: slots.headerActions }),
2836
- slots.emptyState ?? /* @__PURE__ */ jsxs8("div", { className: "mb-6 flex flex-col items-center", children: [
2837
- props.avatar && /* @__PURE__ */ jsx10("div", { className: "mb-3", children: props.avatar }),
2838
- props.welcome && // Un saludo largo (una explicación del piloto) baja a letra de lectura.
2839
- /* @__PURE__ */ jsx10(
2840
- "h1",
2841
- {
2842
- className: typeof props.welcome === "string" && props.welcome.length > 60 ? "max-w-xl text-center text-base font-medium leading-6 text-metal" : "text-center text-3xl font-extrabold tracking-tight text-dark",
2843
- children: props.welcome
2844
- }
2845
- )
2846
- ] }),
2847
- /* @__PURE__ */ jsxs8("div", { className: "w-full max-w-3xl", children: [
2848
- aboveComposer,
2849
- composer,
2850
- props.suggestions && /* @__PURE__ */ jsx10(Suggestions, { items: props.suggestions, onPick: (s) => send(s), onPrefill: (s) => setDraftState((d) => ({ text: s, n: d.n + 1 })) })
2851
- ] })
2852
- ] })
2853
- ) : /* @__PURE__ */ jsxs8(Fragment6, { children: [
2854
- slots.headerActions && /* @__PURE__ */ jsx10("div", { className: "flex shrink-0 justify-end gap-2 px-4 pt-3 sm:px-6", children: slots.headerActions }),
2855
- /* @__PURE__ */ jsxs8(
1512
+ const aboveComposer = plugins.composerAbove.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.composerAbove, ctx, styled: plugins.ghostykitStyled }) : null;
1513
+ const belowComposer = plugins.composerBelow.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.composerBelow, ctx, styled: plugins.ghostykitStyled }) : null;
1514
+ const header = plugins.header.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.header, ctx, styled: plugins.ghostykitStyled, inline: true }) : null;
1515
+ const Empty = plugins.empty?.value;
1516
+ const Waiting = plugins.waiting?.value;
1517
+ const renderDeliveries = (turn, live) => {
1518
+ const custom = (turn.custom ?? []).map((entry, i) => {
1519
+ const owner = plugins.customRenders[entry.name];
1520
+ if (!owner) return null;
1521
+ const C = owner.value;
1522
+ return /* @__PURE__ */ jsx8("div", { ...mark(owner.plugin), children: /* @__PURE__ */ jsx8(C, { ctx, entry, turn, live }) }, `c${i}`);
1523
+ });
1524
+ const extras = plugins.messageAssistant.map((o, i) => /* @__PURE__ */ jsx8("div", { ...mark(o.plugin), children: /* @__PURE__ */ jsx8(o.value, { ctx, turn, live }) }, `a${i}`));
1525
+ return custom.some(Boolean) || extras.length ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
1526
+ custom,
1527
+ extras
1528
+ ] }) : null;
1529
+ };
1530
+ const renderTools = (turn, live) => {
1531
+ const views = (turn.tools ?? []).flatMap((tool) => {
1532
+ const owner = plugins.toolRenders[tool.title ?? ""] ?? (tool.kind ? plugins.toolRenders[tool.kind] : void 0);
1533
+ if (!owner) return [];
1534
+ const C = owner.value;
1535
+ return [
1536
+ /* @__PURE__ */ jsx8("div", { ...mark(owner.plugin), children: /* @__PURE__ */ jsx8(C, { ctx, tool, turn, live }) }, tool.id)
1537
+ ];
1538
+ });
1539
+ return views.length ? /* @__PURE__ */ jsx8("div", { className: "mb-3 flex flex-col gap-2", children: views }) : null;
1540
+ };
1541
+ const renderBefore = plugins.messageBefore.length ? (turn, live) => /* @__PURE__ */ jsx8(Fragment4, { children: plugins.messageBefore.map((o, i) => /* @__PURE__ */ jsx8("div", { ...mark(o.plugin), children: /* @__PURE__ */ jsx8(o.value, { ctx, turn, live }) }, i)) }) : void 0;
1542
+ const blockLangs = Object.keys(plugins.blockRenders);
1543
+ const blocks = blockLangs.length ? {
1544
+ langs: blockLangs,
1545
+ render: (list, turn, live) => /* @__PURE__ */ jsx8("div", { className: "mt-2 flex flex-col gap-2", children: list.map((block, i) => {
1546
+ const owner = plugins.blockRenders[block.lang];
1547
+ const C = owner.value;
1548
+ return /* @__PURE__ */ jsx8("div", { ...mark(owner.plugin), children: /* @__PURE__ */ jsx8(C, { ctx, block, turn, live }) }, i);
1549
+ }) })
1550
+ } : void 0;
1551
+ const wrapAll = (tree) => plugins.wraps.reduceRight((inner, { value: W }) => /* @__PURE__ */ jsx8(W, { ctx, children: inner }), tree);
1552
+ const permissionCards = cards.map((p) => /* @__PURE__ */ jsx8(ConfirmCard, { request: p, onRespond: respond, onSettled: settle, onExpire: pilot.expirePermission }, p.id));
1553
+ return /* @__PURE__ */ jsx8(PilotoContext.Provider, { value: config, children: wrapAll(
1554
+ /* @__PURE__ */ jsx8(
2856
1555
  "div",
2857
1556
  {
2858
- ref: scroller,
2859
- onScroll,
2860
- onWheel: (e) => e.deltaY < 0 && release(),
2861
- onTouchStart: (e) => {
2862
- touchY.current = e.touches[0]?.clientY ?? null;
2863
- },
2864
- onTouchMove: (e) => {
2865
- const y = e.touches[0]?.clientY;
2866
- if (touchY.current != null && y != null && y > touchY.current + 4) release();
2867
- },
2868
- onKeyDown: (e) => ["ArrowUp", "PageUp", "Home"].includes(e.key) && release(),
2869
- className: "gs-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-hidden",
2870
- children: [
2871
- /* @__PURE__ */ jsxs8("div", { ref: column, className: "mx-auto flex w-full max-w-[784px] flex-col gap-6 px-4 py-8 sm:px-6", children: [
2872
- state.history?.hasMoreBefore && (external || props.loadOlder) && /* @__PURE__ */ jsx10(
1557
+ ...rootProps(theme, props.skin),
1558
+ "data-busy": busy || void 0,
1559
+ className: `piloto ${plugins.className} ${props.className ?? ""}`,
1560
+ style: Object.keys(plugins.vars).length ? { ...plugins.vars, ...props.style } : props.style,
1561
+ children: withPanel(empty ? (
1562
+ // Un hilo vacío enseña lo mismo que la portada: la cara, el saludo, la caja y por dónde
1563
+ // empezar. Si no cabe (panel bajo), se desplaza: `safe center` nunca empuja nada fuera.
1564
+ /* @__PURE__ */ jsxs6("div", { className: "gs-scroll flex min-h-0 flex-1 flex-col items-center justify-center overflow-y-auto px-4 pb-[10vh] [justify-content:safe_center]", children: [
1565
+ header && /* @__PURE__ */ jsx8("div", { className: "absolute right-4 top-3 flex gap-2", children: header }),
1566
+ Empty ? /* @__PURE__ */ jsx8("div", { ...mark(plugins.empty.plugin), children: /* @__PURE__ */ jsx8(Empty, { ctx }) }) : /* @__PURE__ */ jsxs6("div", { className: "mb-6 flex flex-col items-center", children: [
1567
+ props.avatar && /* @__PURE__ */ jsx8("div", { className: "mb-3", children: props.avatar }),
1568
+ props.welcome && // Un saludo largo (una explicación del piloto) baja a letra de lectura.
1569
+ /* @__PURE__ */ jsx8(
1570
+ "h1",
1571
+ {
1572
+ className: typeof props.welcome === "string" && props.welcome.length > 60 ? "max-w-xl text-center text-base font-medium leading-6 text-metal" : "text-center text-3xl font-extrabold tracking-tight text-dark",
1573
+ children: props.welcome
1574
+ }
1575
+ )
1576
+ ] }),
1577
+ /* @__PURE__ */ jsxs6("div", { className: "w-full max-w-3xl", children: [
1578
+ aboveComposer,
1579
+ composer,
1580
+ belowComposer,
1581
+ props.suggestions && /* @__PURE__ */ jsx8(Suggestions, { items: props.suggestions, onPick: (s) => send(s), onPrefill: (s) => setDraftState((d) => ({ text: s, n: d.n + 1 })) })
1582
+ ] })
1583
+ ] })
1584
+ ) : /* @__PURE__ */ jsxs6(Fragment4, { children: [
1585
+ header && /* @__PURE__ */ jsx8("div", { className: "flex shrink-0 justify-end gap-2 px-4 pt-3 sm:px-6", children: header }),
1586
+ /* @__PURE__ */ jsxs6(
1587
+ "div",
1588
+ {
1589
+ ref: scroller,
1590
+ onScroll,
1591
+ onWheel: (e) => e.deltaY < 0 && release(),
1592
+ onTouchStart: (e) => {
1593
+ touchY.current = e.touches[0]?.clientY ?? null;
1594
+ },
1595
+ onTouchMove: (e) => {
1596
+ const y = e.touches[0]?.clientY;
1597
+ if (touchY.current != null && y != null && y > touchY.current + 4) release();
1598
+ },
1599
+ onKeyDown: (e) => ["ArrowUp", "PageUp", "Home"].includes(e.key) && release(),
1600
+ className: "gs-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-hidden",
1601
+ children: [
1602
+ /* @__PURE__ */ jsxs6("div", { ref: column, className: "mx-auto flex w-full max-w-[784px] flex-col gap-6 px-4 py-8 sm:px-6", children: [
1603
+ plugins.aboveThread.length > 0 && /* @__PURE__ */ jsx8(Views, { items: plugins.aboveThread, ctx, styled: plugins.ghostykitStyled }),
1604
+ state.history?.hasMoreBefore && (external || props.loadOlder) && /* @__PURE__ */ jsx8(
1605
+ "button",
1606
+ {
1607
+ type: "button",
1608
+ onClick: loadOlder,
1609
+ disabled: pilot.loadingOlder,
1610
+ className: "self-center rounded-full border border-border-primary px-3 py-1 text-sm text-text-secondary transition hover:text-text-primary disabled:opacity-60",
1611
+ children: pilot.loadingOlder ? t.loadingOlder : t.loadOlder
1612
+ }
1613
+ ),
1614
+ turns.map((turn, i) => {
1615
+ const isLast = i === turns.length - 1;
1616
+ const live = busy && isLast;
1617
+ return /* @__PURE__ */ jsxs6("div", { "data-turn": i - olderCount, children: [
1618
+ turn.role === "user" ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
1619
+ /* @__PURE__ */ jsx8(UserBubble, { turn }),
1620
+ plugins.messageUser.map((o, j) => /* @__PURE__ */ jsx8("div", { ...mark(o.plugin), children: /* @__PURE__ */ jsx8(o.value, { ctx, turn }) }, j))
1621
+ ] }) : /* @__PURE__ */ jsx8(
1622
+ AgentBubble,
1623
+ {
1624
+ turn,
1625
+ live,
1626
+ onRetry: isLast && features.retry !== false ? pilot.retry : void 0,
1627
+ onOption: isLast && !busy && features.replyOptions !== false ? (text) => send(text) : void 0,
1628
+ showUsage: features.usage !== false,
1629
+ showCopy: features.copy !== false,
1630
+ showDuration: features.duration !== false,
1631
+ renderDeliveries,
1632
+ renderTools,
1633
+ renderBefore,
1634
+ blocks,
1635
+ narration: features.narration !== false,
1636
+ avatar: plugins.avatar ? /* @__PURE__ */ jsx8("span", { ...mark(plugins.avatar.plugin), children: /* @__PURE__ */ jsx8(plugins.avatar.value, { ctx, turn }) }) : void 0
1637
+ }
1638
+ ),
1639
+ turn.role === "assistant" && /* @__PURE__ */ jsx8(TurnNotes, { turn })
1640
+ ] }, i - olderCount);
1641
+ }),
1642
+ busy && state.stalled && /* @__PURE__ */ jsx8("p", { className: "text-sm text-text-tertiary", children: state.stalled.message ?? t.stalled }),
1643
+ state.queued.map((q, i) => (
1644
+ // Esperan a que termine el run vivo (el servidor no los dejó entrar): salen solos.
1645
+ /* @__PURE__ */ jsxs6("div", { "data-queued": true, className: "flex flex-col items-end gap-1 opacity-60", children: [
1646
+ /* @__PURE__ */ jsx8(UserBubble, { turn: { role: "user", text: q.text, ...q.attachments ? { attachments: q.attachments } : {} } }),
1647
+ /* @__PURE__ */ jsx8("span", { className: "text-xs text-text-tertiary", children: t.queued })
1648
+ ] }, `q${i}`)
1649
+ )),
1650
+ permissionCards,
1651
+ busy && !toolRunning && !cards.length && (Waiting ? /* @__PURE__ */ jsx8("div", { ...mark(plugins.waiting.plugin), children: /* @__PURE__ */ jsx8(Waiting, { ctx, step: state.step, since: state.turnStartedAt }) }) : /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1.5", children: [
1652
+ /* @__PURE__ */ jsx8(TypingDots, {}),
1653
+ /* @__PURE__ */ jsx8(WaitLabel, { since: state.turnStartedAt, step: state.step, showDuration: features.duration !== false })
1654
+ ] })),
1655
+ notice && /* @__PURE__ */ jsx8("p", { className: "text-sm text-text-tertiary", children: notice }),
1656
+ error && /* @__PURE__ */ jsx8("p", { className: "text-sm text-text-danger", children: error })
1657
+ ] }),
1658
+ /* @__PURE__ */ jsx8("div", { ref: spacer, "aria-hidden": true })
1659
+ ]
1660
+ }
1661
+ ),
1662
+ /* @__PURE__ */ jsxs6("div", { className: "relative mx-auto w-full max-w-[784px] px-4 pb-4 sm:px-6 sm:pb-6", children: [
1663
+ !atBottom && /* @__PURE__ */ jsx8(
2873
1664
  "button",
2874
1665
  {
2875
1666
  type: "button",
2876
- onClick: loadOlder,
2877
- disabled: pilot.loadingOlder,
2878
- className: "self-center rounded-full border border-border-primary px-3 py-1 text-sm text-text-secondary transition hover:text-text-primary disabled:opacity-60",
2879
- children: pilot.loadingOlder ? t.loadingOlder : t.loadOlder
1667
+ onClick: toBottom,
1668
+ "aria-label": t.goToLatest,
1669
+ className: "absolute -top-12 left-1/2 z-10 flex h-9 w-9 -translate-x-1/2 items-center justify-center rounded-full border border-border-primary bg-background-secondary text-text-secondary shadow-lg transition hover:text-text-primary",
1670
+ children: /* @__PURE__ */ jsx8(ArrowDown, { className: "h-4 w-4" })
2880
1671
  }
2881
1672
  ),
2882
- turns.map((turn, i) => {
2883
- const isLast = i === turns.length - 1;
2884
- const live = busy && isLast;
2885
- return /* @__PURE__ */ jsxs8("div", { "data-turn": i - olderCount, children: [
2886
- turn.role === "user" ? /* @__PURE__ */ jsx10(UserBubble, { turn }) : /* @__PURE__ */ jsx10(
2887
- AgentBubble,
2888
- {
2889
- turn,
2890
- live,
2891
- onRetry: isLast && features.retry !== false ? pilot.retry : void 0,
2892
- onOption: isLast && !busy && features.replyOptions !== false ? (text) => send(text) : void 0,
2893
- showUsage: features.usage !== false,
2894
- showCopy: features.copy !== false,
2895
- showDuration: features.duration !== false,
2896
- renderDeliveries: slots.renderDeliveries
2897
- }
2898
- ),
2899
- turn.role === "assistant" && /* @__PURE__ */ jsx10(TurnNotes, { turn })
2900
- ] }, i - olderCount);
2901
- }),
2902
- busy && state.stalled && /* @__PURE__ */ jsx10("p", { className: "text-sm text-text-tertiary", children: state.stalled.message ?? t.stalled }),
2903
- state.queued.map((q, i) => (
2904
- // Esperan a que termine el run vivo (el servidor no los dejó entrar): salen solos.
2905
- /* @__PURE__ */ jsxs8("div", { "data-queued": true, className: "flex flex-col items-end gap-1 opacity-60", children: [
2906
- /* @__PURE__ */ jsx10(UserBubble, { turn: { role: "user", text: q.text, ...q.attachments ? { attachments: q.attachments } : {} } }),
2907
- /* @__PURE__ */ jsx10("span", { className: "text-xs text-text-tertiary", children: t.queued })
2908
- ] }, `q${i}`)
2909
- )),
2910
- permissionCards,
2911
- busy && !toolRunning && !cards.length && /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-1.5", children: [
2912
- /* @__PURE__ */ jsx10(TypingDots, {}),
2913
- /* @__PURE__ */ jsx10(WaitLabel, { since: state.turnStartedAt, step: state.step, showDuration: features.duration !== false })
1673
+ state.cutRun && !busy && // El servidor cortó la respuesta (deploy, caja caída): la persona decide si sigue.
1674
+ /* @__PURE__ */ jsxs6("div", { "data-cut-run": true, role: "status", className: "mb-2 flex items-center gap-3 rounded-2xl border border-border-primary bg-background-secondary px-3 py-2 text-sm text-text-secondary", children: [
1675
+ /* @__PURE__ */ jsx8("span", { className: "min-w-0 flex-1", children: state.cutRun.reason ?? t.runCut }),
1676
+ /* @__PURE__ */ jsx8(
1677
+ "button",
1678
+ {
1679
+ type: "button",
1680
+ onClick: () => void pilot.continueRun(),
1681
+ className: "shrink-0 rounded-full bg-brand-500 px-3 py-1 text-sm font-medium text-white transition hover:bg-brand-600",
1682
+ children: t.continueRun
1683
+ }
1684
+ )
2914
1685
  ] }),
2915
- notice && /* @__PURE__ */ jsx10("p", { className: "text-sm text-text-tertiary", children: notice }),
2916
- error && /* @__PURE__ */ jsx10("p", { className: "text-sm text-text-danger", children: error })
2917
- ] }),
2918
- /* @__PURE__ */ jsx10("div", { ref: spacer, "aria-hidden": true })
2919
- ]
1686
+ aboveComposer,
1687
+ composer,
1688
+ belowComposer
1689
+ ] })
1690
+ ] }))
2920
1691
  }
2921
- ),
2922
- /* @__PURE__ */ jsxs8("div", { className: "relative mx-auto w-full max-w-[784px] px-4 pb-4 sm:px-6 sm:pb-6", children: [
2923
- !atBottom && /* @__PURE__ */ jsx10(
2924
- "button",
2925
- {
2926
- type: "button",
2927
- onClick: toBottom,
2928
- "aria-label": t.goToLatest,
2929
- className: "absolute -top-12 left-1/2 z-10 flex h-9 w-9 -translate-x-1/2 items-center justify-center rounded-full border border-border-primary bg-background-secondary text-text-secondary shadow-lg transition hover:text-text-primary",
2930
- children: /* @__PURE__ */ jsx10(ArrowDown, { className: "h-4 w-4" })
2931
- }
2932
- ),
2933
- state.cutRun && !busy && // El servidor cortó la respuesta (deploy, caja caída): la persona decide si sigue.
2934
- /* @__PURE__ */ jsxs8("div", { "data-cut-run": true, role: "status", className: "mb-2 flex items-center gap-3 rounded-2xl border border-border-primary bg-background-secondary px-3 py-2 text-sm text-text-secondary", children: [
2935
- /* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1", children: state.cutRun.reason ?? t.runCut }),
2936
- /* @__PURE__ */ jsx10(
2937
- "button",
2938
- {
2939
- type: "button",
2940
- onClick: () => void pilot.continueRun(),
2941
- className: "shrink-0 rounded-full bg-brand-500 px-3 py-1 text-sm font-medium text-white transition hover:bg-brand-600",
2942
- children: t.continueRun
2943
- }
2944
- )
2945
- ] }),
2946
- aboveComposer,
2947
- composer
2948
- ] })
2949
- ] })) }) });
1692
+ )
1693
+ ) });
2950
1694
  });
1695
+ var warned = /* @__PURE__ */ new Set();
1696
+ function warnOnce(message) {
1697
+ if (warned.has(message)) return;
1698
+ warned.add(message);
1699
+ console.warn(`[ghostykit] ${message}`);
1700
+ }
1701
+ function pluginAttrs(plugin, styled) {
1702
+ return styled.has(plugin) ? { "data-plugin": plugin } : { "data-plugin": plugin, "data-piloto-isolate": "" };
1703
+ }
1704
+ function Views({ items, ctx, inline, className, styled }) {
1705
+ return /* @__PURE__ */ jsx8(Fragment4, { children: items.map(
1706
+ ({ plugin, value: C }) => inline ? /* @__PURE__ */ jsx8("span", { ...pluginAttrs(plugin, styled), style: { display: "contents" }, children: /* @__PURE__ */ jsx8(C, { ctx }) }, plugin) : /* @__PURE__ */ jsx8("div", { ...pluginAttrs(plugin, styled), className, children: /* @__PURE__ */ jsx8(C, { ctx }) }, plugin)
1707
+ ) });
1708
+ }
2951
1709
  function TurnNotes({ turn }) {
2952
1710
  const { strings: t } = usePiloto();
2953
1711
  const failed = (turn.custom ?? []).filter((c) => c.name === "ghosty.attachments_failed").flatMap((c) => (c.value?.files ?? []).filter((f) => f?.name));
2954
1712
  if (!failed.length && !turn.truncated) return null;
2955
- return /* @__PURE__ */ jsxs8("div", { "data-turn-notes": true, className: "mt-1 flex flex-col gap-0.5 text-xs text-text-tertiary", children: [
2956
- failed.map((f, i) => /* @__PURE__ */ jsx10("p", { children: t.attachmentFailed(f.name, f.reason) }, i)),
2957
- turn.truncated && /* @__PURE__ */ jsx10("p", { children: turn.truncated.reason ?? t.truncated })
1713
+ return /* @__PURE__ */ jsxs6("div", { "data-turn-notes": true, className: "mt-1 flex flex-col gap-0.5 text-xs text-text-tertiary", children: [
1714
+ failed.map((f, i) => /* @__PURE__ */ jsx8("p", { children: t.attachmentFailed(f.name, f.reason) }, i)),
1715
+ turn.truncated && /* @__PURE__ */ jsx8("p", { children: turn.truncated.reason ?? t.truncated })
2958
1716
  ] });
2959
1717
  }
2960
1718
 
2961
1719
  // src/react/Subagents.tsx
2962
- import { useState as useState10 } from "react";
2963
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1720
+ import { useState as useState9 } from "react";
1721
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2964
1722
  var DOT = {
2965
1723
  running: "bg-brand-500 animate-pulse",
2966
1724
  completed: "bg-text-tertiary",
@@ -2969,33 +1727,33 @@ var DOT = {
2969
1727
  };
2970
1728
  function Subagents({ pilot, defaultOpen = false, showFinished = true, className = "" }) {
2971
1729
  const { strings: t } = usePiloto();
2972
- const [open, setOpen] = useState10(defaultOpen);
1730
+ const [open, setOpen] = useState9(defaultOpen);
2973
1731
  const all = pilot.subagents;
2974
1732
  const tasks = showFinished ? all : all.filter((x) => x.status === "running");
2975
1733
  if (!tasks.length) return null;
2976
1734
  const running = all.filter((x) => x.status === "running").length;
2977
- return /* @__PURE__ */ jsxs9("div", { "data-subagents": true, className: `mb-2 rounded-2xl border border-border-primary bg-background-secondary text-sm ${className}`, children: [
2978
- /* @__PURE__ */ jsxs9("button", { type: "button", onClick: () => setOpen((o) => !o), "aria-expanded": open, className: "flex w-full items-center gap-2 px-3 py-2 text-left text-text-secondary", children: [
2979
- /* @__PURE__ */ jsx11("span", { className: `h-2 w-2 shrink-0 rounded-full ${running ? DOT.running : DOT.completed}` }),
2980
- /* @__PURE__ */ jsx11("span", { className: "flex-1 truncate", children: running ? t.subagentsWorking(running) : t.subagentsDone(tasks.length) }),
2981
- /* @__PURE__ */ jsx11("span", { "aria-hidden": true, className: `text-text-tertiary transition-transform ${open ? "rotate-180" : ""}`, children: "\u2304" })
1735
+ return /* @__PURE__ */ jsxs7("div", { "data-subagents": true, className: `mb-2 rounded-2xl border border-border-primary bg-background-secondary text-sm ${className}`, children: [
1736
+ /* @__PURE__ */ jsxs7("button", { type: "button", onClick: () => setOpen((o) => !o), "aria-expanded": open, className: "flex w-full items-center gap-2 px-3 py-2 text-left text-text-secondary", children: [
1737
+ /* @__PURE__ */ jsx9("span", { className: `h-2 w-2 shrink-0 rounded-full ${running ? DOT.running : DOT.completed}` }),
1738
+ /* @__PURE__ */ jsx9("span", { className: "flex-1 truncate", children: running ? t.subagentsWorking(running) : t.subagentsDone(tasks.length) }),
1739
+ /* @__PURE__ */ jsx9("span", { "aria-hidden": true, className: `text-text-tertiary transition-transform ${open ? "rotate-180" : ""}`, children: "\u2304" })
2982
1740
  ] }),
2983
- open && /* @__PURE__ */ jsx11("ul", { className: "flex flex-col border-t border-border-primary", children: tasks.map((task) => /* @__PURE__ */ jsx11(SubagentRow, { task, onStop: () => void pilot.stopSubagent(task.id) }, task.id)) })
1741
+ open && /* @__PURE__ */ jsx9("ul", { className: "flex flex-col border-t border-border-primary", children: tasks.map((task) => /* @__PURE__ */ jsx9(SubagentRow, { task, onStop: () => void pilot.stopSubagent(task.id) }, task.id)) })
2984
1742
  ] });
2985
1743
  }
2986
1744
  function SubagentRow({ task, onStop }) {
2987
1745
  const { strings: t } = usePiloto();
2988
1746
  const line = task.status === "running" ? task.lastTool : task.summary ?? task.lastTool;
2989
- return /* @__PURE__ */ jsxs9("li", { "data-subagent": task.id, "data-status": task.status, className: "flex items-center gap-2 px-3 py-2", children: [
2990
- /* @__PURE__ */ jsx11("span", { className: `h-2 w-2 shrink-0 rounded-full ${DOT[task.status] ?? DOT.completed}` }),
2991
- /* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1", children: [
2992
- /* @__PURE__ */ jsx11("div", { className: "truncate text-text-primary", children: task.title ?? task.id }),
2993
- /* @__PURE__ */ jsxs9("div", { className: "truncate text-xs text-text-tertiary", children: [
1747
+ return /* @__PURE__ */ jsxs7("li", { "data-subagent": task.id, "data-status": task.status, className: "flex items-center gap-2 px-3 py-2", children: [
1748
+ /* @__PURE__ */ jsx9("span", { className: `h-2 w-2 shrink-0 rounded-full ${DOT[task.status] ?? DOT.completed}` }),
1749
+ /* @__PURE__ */ jsxs7("div", { className: "min-w-0 flex-1", children: [
1750
+ /* @__PURE__ */ jsx9("div", { className: "truncate text-text-primary", children: task.title ?? task.id }),
1751
+ /* @__PURE__ */ jsxs7("div", { className: "truncate text-xs text-text-tertiary", children: [
2994
1752
  t.subagentStatus[task.status] ?? task.status,
2995
1753
  line ? ` \xB7 ${line}` : ""
2996
1754
  ] })
2997
1755
  ] }),
2998
- task.status === "running" && /* @__PURE__ */ jsx11("button", { type: "button", onClick: onStop, className: "shrink-0 rounded-full border border-border-primary px-2.5 py-0.5 text-xs text-text-secondary transition hover:text-text-danger", children: t.subagentStop })
1756
+ task.status === "running" && /* @__PURE__ */ jsx9("button", { type: "button", onClick: onStop, className: "shrink-0 rounded-full border border-border-primary px-2.5 py-0.5 text-xs text-text-secondary transition hover:text-text-danger", children: t.subagentStop })
2999
1757
  ] });
3000
1758
  }
3001
1759
  export {
@@ -3041,8 +1799,10 @@ export {
3041
1799
  createAgUiTransport,
3042
1800
  createPilotState,
3043
1801
  Piloto as default,
1802
+ definePlugin,
3044
1803
  dropLastExchange,
3045
1804
  expiryMs,
1805
+ extractBlocks,
3046
1806
  extractReplyOptions,
3047
1807
  formatDuration,
3048
1808
  groupSteps,
@@ -3053,12 +1813,15 @@ export {
3053
1813
  messagesToTurns,
3054
1814
  mimeOf,
3055
1815
  narrationToMarkdown,
1816
+ pluginPoints,
1817
+ pluginRequires,
3056
1818
  prependHistory,
3057
1819
  queueMessage,
3058
1820
  readSse,
3059
1821
  reduceAgUiEvent,
3060
1822
  renderInline,
3061
1823
  resolvePermission,
1824
+ resolvePlugins,
3062
1825
  sendFailed,
3063
1826
  splitNarration,
3064
1827
  stepLabel,