ghostykit 0.6.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,76 @@ 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-FWBUIGTC.js";
35
28
  import {
36
29
  PilotHttpError,
37
30
  createAgUiTransport,
38
31
  readSse
39
32
  } from "../chunks/chunk-TMEHQZWL.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
+ X,
74
+ definePlugin,
75
+ isDev,
76
+ mimeOf,
77
+ pluginPoints,
78
+ pluginRequires,
79
+ resolvePlugins,
80
+ rootProps,
81
+ usePiloto
82
+ } from "../chunks/chunk-ZIUOXG7B.js";
83
+ import {
84
+ TOOL_VERBS_EN,
85
+ TOOL_VERBS_ES,
86
+ groupSteps,
87
+ isRunning,
88
+ stepLabel,
89
+ summarizeTools,
90
+ toolName,
91
+ toolNames
92
+ } from "../chunks/chunk-UUH37KXA.js";
40
93
  import {
41
94
  applyReferences,
42
95
  cleanUserText,
@@ -44,790 +97,16 @@ import {
44
97
  systemNoteOf
45
98
  } from "../chunks/chunk-6U545WJG.js";
46
99
 
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
100
  // src/react/Piloto.tsx
822
101
  import {
823
- forwardRef as forwardRef3,
824
- useCallback as useCallback3,
825
- useEffect as useEffect6,
102
+ forwardRef,
103
+ useCallback as useCallback2,
104
+ useEffect as useEffect5,
826
105
  useImperativeHandle,
827
106
  useLayoutEffect,
828
- useMemo as useMemo4,
829
- useRef as useRef5,
830
- useState as useState9
107
+ useMemo as useMemo3,
108
+ useRef as useRef4,
109
+ useState as useState8
831
110
  } from "react";
832
111
 
833
112
  // src/react/Bubble.tsx
@@ -835,184 +114,6 @@ import { useState as useState5 } from "react";
835
114
 
836
115
  // src/react/ConfirmCard.tsx
837
116
  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
117
  import { jsx, jsxs } from "react/jsx-runtime";
1017
118
  var isRejectOption = (o) => /^reject|deny|cancel/i.test(o.kind ?? o.optionId);
1018
119
  var mmss = (ms) => {
@@ -1101,10 +202,10 @@ function DecisionLines({ decisions }) {
1101
202
  const { strings: t } = usePiloto();
1102
203
  if (!decisions.length) return null;
1103
204
  return /* @__PURE__ */ jsx("ul", { className: "piloto-decisions", children: decisions.map((d) => {
1104
- const Icon2 = d.status === "approved" ? CircleCheck : d.status === "expired" ? Clock : CircleSlash;
205
+ const Icon = d.status === "approved" ? CircleCheck : d.status === "expired" ? Clock : CircleSlash;
1105
206
  const label = d.status === "approved" ? t.decisionApproved : d.status === "rejected" ? t.decisionRejected : d.status === "cancelled" ? t.decisionCancelled : t.decisionExpired;
1106
207
  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 }),
208
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0", "aria-hidden": true }),
1108
209
  /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
1109
210
  /* @__PURE__ */ jsxs("strong", { children: [
1110
211
  label,
@@ -1123,11 +224,11 @@ function DecisionLines({ decisions }) {
1123
224
  }
1124
225
 
1125
226
  // src/react/Markdown.tsx
1126
- import { useState as useState3 } from "react";
227
+ import { useContext as useContext2, useState as useState3 } from "react";
1127
228
  import { Streamdown } from "streamdown";
1128
229
 
1129
230
  // 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";
231
+ import { createContext, memo, useContext, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
1131
232
  import { Block } from "streamdown";
1132
233
  import { jsx as jsx2 } from "react/jsx-runtime";
1133
234
  var CHUNK_MS = 400;
@@ -1195,9 +296,9 @@ function useTextAtPace(text, live, whole = false) {
1195
296
  if (!animate.current) return { visible: text, animate: false };
1196
297
  return { visible: text.slice(0, Math.min(released, text.length)), animate: true };
1197
298
  }
1198
- var ChunksContext = createContext3(null);
299
+ var ChunksContext = createContext(null);
1199
300
  function useChunkRegistry() {
1200
- return useMemo2(() => /* @__PURE__ */ new Map(), []);
301
+ return useMemo(() => /* @__PURE__ */ new Map(), []);
1201
302
  }
1202
303
  var UNTOUCHED = /* @__PURE__ */ new Set(["pre", "code", "svg", "math", "script", "style"]);
1203
304
  var STYLE = `gs-tramo ${CHUNK_MS}ms linear both`;
@@ -1253,9 +354,9 @@ function chunksPlugin(state) {
1253
354
  };
1254
355
  }
1255
356
  var ChunkedBlock = memo(function ChunkedBlock2(props) {
1256
- const ctx = useContext3(ChunksContext);
357
+ const ctx = useContext(ChunksContext);
1257
358
  const key = `${ctx?.part ?? 0}-${props.index}`;
1258
- const plugins = useMemo2(() => {
359
+ const plugins = useMemo(() => {
1259
360
  if (!ctx) return props.rehypePlugins;
1260
361
  let state = ctx.registry.get(key);
1261
362
  if (!state) {
@@ -1323,7 +424,8 @@ var linkSafety = { enabled: true, renderModal: (p) => /* @__PURE__ */ jsx3(LinkS
1323
424
  function Markdown({ children, animate = false, className }) {
1324
425
  const { markdown } = usePiloto();
1325
426
  const registry = useChunkRegistry();
1326
- const text = markdown.transform ? markdown.transform(children) : children;
427
+ const turn = useContext2(TurnContext);
428
+ const text = markdown.transform ? markdown.transform(children, turn) : children;
1327
429
  const onClick = markdown.onLinkClick ? (e) => {
1328
430
  const a = e.target.closest("a[href]");
1329
431
  if (!a) return;
@@ -1675,17 +777,21 @@ function TurnFooter({ text, seconds, onRetry, showCopy = true, showDuration = tr
1675
777
  ] })
1676
778
  ] });
1677
779
  }
1678
- function AgentBubble({ turn, live, onRetry, onOption, showUsage = true, showCopy = true, showDuration = true, renderDeliveries }) {
780
+ function AgentBubble({ turn, live, onRetry, onOption, showUsage = true, showCopy = true, showDuration = true, renderDeliveries, renderTools, renderBefore, blocks }) {
1679
781
  const { strings: t } = usePiloto();
1680
- const narrated = splitNarration(turn.text ?? "", turn.cuts);
782
+ const extracted = blocks ? extractBlocks(turn.text ?? "", blocks.langs, turn.cuts) : { text: turn.text ?? "", cuts: turn.cuts, blocks: [] };
783
+ const closed = !live && !turn.tools?.some((x) => x.status === "pending");
784
+ const narrated = splitNarration(extracted.text, extracted.cuts, closed);
1681
785
  const visible = narrated.rest;
1682
786
  const options = onOption && !live && visible ? extractReplyOptions(visible) : null;
1683
- return /* @__PURE__ */ jsxs4("div", { className: "gc-msg-agent min-w-0 max-w-[90%]", children: [
787
+ return /* @__PURE__ */ jsx6(TurnContext.Provider, { value: turn, children: /* @__PURE__ */ jsxs4("div", { className: "gc-msg-agent min-w-0 max-w-[90%]", children: [
1684
788
  turn.thought && /* @__PURE__ */ jsxs4("details", { className: "mb-3 text-xs text-text-secondary", children: [
1685
789
  /* @__PURE__ */ jsx6("summary", { className: "cursor-pointer select-none", children: t.thinking }),
1686
790
  /* @__PURE__ */ jsx6("p", { className: "mt-2 whitespace-pre-wrap border-l-2 border-border-secondary pl-3", children: turn.thought })
1687
791
  ] }),
1688
792
  turn.tools && turn.tools.length > 0 && /* @__PURE__ */ jsx6(AgentSteps, { tools: turn.tools, live }),
793
+ turn.tools && turn.tools.length > 0 && renderTools?.(turn, live),
794
+ renderBefore?.(turn, live),
1689
795
  narrated.steps.length > 0 && /* @__PURE__ */ jsx6(Narration, { steps: narrated.steps }),
1690
796
  options ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
1691
797
  options.before && /* @__PURE__ */ jsx6(AnswerText, { text: options.before, live }),
@@ -1694,593 +800,21 @@ function AgentBubble({ turn, live, onRetry, onOption, showUsage = true, showCopy
1694
800
  // Cada tramo nuevo tras una herramienta monta su propio ritmo (empieza de cero).
1695
801
  visible && /* @__PURE__ */ jsx6(AnswerText, { text: visible, live }, `r${narrated.steps.length}`)
1696
802
  ),
803
+ extracted.blocks.length > 0 && blocks.render(extracted.blocks, turn, live),
1697
804
  renderDeliveries?.(turn, live),
1698
805
  turn.decisions?.length ? /* @__PURE__ */ jsx6(DecisionLines, { decisions: turn.decisions }) : null,
1699
806
  showUsage && turn.usage && /* @__PURE__ */ jsx6(UsageStats, { ...turn.usage }),
1700
807
  turn.text && !live && /* @__PURE__ */ jsx6(TurnFooter, { text: turn.text, seconds: turn.seconds, onRetry, showCopy, showDuration })
1701
- ] });
1702
- }
1703
-
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
808
  ] }) });
2222
809
  }
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
810
 
2277
811
  // src/react/Status.tsx
2278
812
  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";
813
+ import { useEffect as useEffect3, useState as useState6 } from "react";
814
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2281
815
  var STILL_WORKING_S = 90;
2282
816
  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)) });
817
+ 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
818
  }
2285
819
  function WaitLabel({
2286
820
  since,
@@ -2288,9 +822,9 @@ function WaitLabel({
2288
822
  step
2289
823
  }) {
2290
824
  const { strings: t } = usePiloto();
2291
- const [mounted] = useState7(() => Date.now());
2292
- const [now, setNow] = useState7(() => Date.now());
2293
- useEffect4(() => {
825
+ const [mounted] = useState6(() => Date.now());
826
+ const [now, setNow] = useState6(() => Date.now());
827
+ useEffect3(() => {
2294
828
  const id = setInterval(() => setNow(Date.now()), 1e3);
2295
829
  return () => clearInterval(id);
2296
830
  }, []);
@@ -2298,8 +832,8 @@ function WaitLabel({
2298
832
  const phrases = t.thinkingPhrases;
2299
833
  const stepText = step ? t.steps?.[step] : void 0;
2300
834
  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(
835
+ 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: [
836
+ /* @__PURE__ */ jsx7(AnimatePresence2, { mode: "wait", initial: false, children: /* @__PURE__ */ jsx7(
2303
837
  motion2.span,
2304
838
  {
2305
839
  className: "gc-wait-text",
@@ -2311,7 +845,7 @@ function WaitLabel({
2311
845
  },
2312
846
  what
2313
847
  ) }),
2314
- showDuration && /* @__PURE__ */ jsxs7("span", { className: "tabular-nums text-text-tertiary", children: [
848
+ showDuration && /* @__PURE__ */ jsxs5("span", { className: "tabular-nums text-text-tertiary", children: [
2315
849
  "\xA0\xB7 ",
2316
850
  formatDuration(seconds)
2317
851
  ] })
@@ -2319,7 +853,7 @@ function WaitLabel({
2319
853
  }
2320
854
 
2321
855
  // src/react/usePilot.ts
2322
- import { useCallback as useCallback2, useEffect as useEffect5, useMemo as useMemo3, useRef as useRef4, useState as useState8 } from "react";
856
+ import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef3, useState as useState7 } from "react";
2323
857
  var MAX_QUEUE_TRIES = 3;
2324
858
  var uuid = () => {
2325
859
  const c = globalThis.crypto;
@@ -2353,19 +887,29 @@ function fromUserMessage(content) {
2353
887
  return { text, attachments };
2354
888
  }
2355
889
  function usePilot(transport, opts = {}) {
2356
- const [state, setState] = useState8(() => createPilotState(opts.initialTurns ?? []));
2357
- const ref = useRef4(state);
2358
- const optsRef = useRef4(opts);
890
+ const [state, setState] = useState7(() => createPilotState(opts.initialTurns ?? []));
891
+ const ref = useRef3(state);
892
+ const optsRef = useRef3(opts);
2359
893
  optsRef.current = opts;
2360
- const transportRef = useRef4(transport);
894
+ const transportRef = useRef3(transport);
2361
895
  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(() => {
896
+ const [threadId] = useState7(() => opts.threadId ?? uuid());
897
+ const ctrl = useRef3(null);
898
+ const liveRun = useRef3(null);
899
+ const answers = useRef3(/* @__PURE__ */ new Map());
900
+ const queuedTries = useRef3(/* @__PURE__ */ new Map());
901
+ const flushRef = useRef3(() => {
2368
902
  });
903
+ const turnListeners = useRef3(/* @__PURE__ */ new Set());
904
+ const onTurn = useCallback((fn) => {
905
+ turnListeners.current.add(fn);
906
+ return () => void turnListeners.current.delete(fn);
907
+ }, []);
908
+ const listeners = useRef3(/* @__PURE__ */ new Set());
909
+ const subscribe = useCallback((fn) => {
910
+ listeners.current.add(fn);
911
+ return () => void listeners.current.delete(fn);
912
+ }, []);
2369
913
  const baseInput = () => {
2370
914
  const ctx = optsRef.current.context;
2371
915
  return { threadId, runId: uuid(), messages: [], tools: [], context: toAgUiContext(typeof ctx === "function" ? ctx() : ctx), state: {}, forwardedProps: extraProps() };
@@ -2374,19 +918,24 @@ function usePilot(transport, opts = {}) {
2374
918
  const f = optsRef.current.forwardedProps;
2375
919
  return (typeof f === "function" ? f() : f) ?? {};
2376
920
  };
2377
- const commit = useCallback2((next) => {
921
+ const commit = useCallback((next) => {
2378
922
  if (next === ref.current) return;
2379
923
  ref.current = next;
2380
924
  setState(next);
2381
925
  const o = optsRef.current;
2382
926
  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);
927
+ if (e.type === "turn-start") {
928
+ o.onTurnStart?.();
929
+ for (const fn of turnListeners.current) fn({ type: "start" });
930
+ } else if (e.type === "turn-done") {
931
+ const info = { text: e.text, runId: e.runId, seconds: e.seconds, error: e.error, interrupted: e.interrupted };
932
+ o.onTurnDone?.(info);
933
+ for (const fn of turnListeners.current) fn({ type: "done", info });
934
+ } else if (e.type === "title") o.onTitle?.(e.title);
2386
935
  else if (e.type === "state") o.onState?.(e.state);
2387
936
  }
2388
937
  }, []);
2389
- const runNow = useCallback2(
938
+ const runNow = useCallback(
2390
939
  async (partial, reopening = false) => {
2391
940
  const t = transportRef.current;
2392
941
  if (!t) return;
@@ -2405,6 +954,7 @@ function usePilot(transport, opts = {}) {
2405
954
  (e) => {
2406
955
  if (c.signal.aborted) return;
2407
956
  optsRef.current.onEvent?.(e);
957
+ for (const fn of listeners.current) fn(e);
2408
958
  if (e.type === "RUN_FINISHED" || e.type === "RUN_ERROR") {
2409
959
  ended = true;
2410
960
  liveRun.current = null;
@@ -2465,17 +1015,31 @@ function usePilot(transport, opts = {}) {
2465
1015
  },
2466
1016
  [commit, threadId]
2467
1017
  );
2468
- useEffect5(() => {
2469
- if (!transport || !opts.threadId) return;
2470
- void runNow({ messages: [] }, true);
2471
- return () => ctrl.current?.abort();
1018
+ const sentInitial = useRef3(false);
1019
+ useEffect4(() => {
1020
+ if (!transport) return;
1021
+ let alive = true;
1022
+ const sendInitial = () => {
1023
+ const prompt = optsRef.current.initialPrompt;
1024
+ if (!alive || !prompt || sentInitial.current) return;
1025
+ sentInitial.current = true;
1026
+ void sendRef.current(prompt);
1027
+ };
1028
+ if (opts.threadId) void runNow({ messages: [] }, true).then(sendInitial);
1029
+ else setTimeout(sendInitial, 0);
1030
+ return () => {
1031
+ alive = false;
1032
+ ctrl.current?.abort();
1033
+ };
2472
1034
  }, [transport]);
2473
1035
  const pendingResume = () => [
2474
1036
  ...ref.current.permissions.filter((p) => !answers.current.has(p.id)).map((p) => ({ interruptId: p.id, status: "cancelled" })),
2475
1037
  ...[...answers.current.entries()].map(([interruptId, a]) => ({ interruptId, ...a }))
2476
1038
  ];
1039
+ const sendRef = useRef3(async () => {
1040
+ });
2477
1041
  const steerOn = () => optsRef.current.steer ?? ref.current.agentState.caps?.steer === true;
2478
- const send = useCallback2(
1042
+ const send = useCallback(
2479
1043
  async (text, attachments = [], _extra = {}) => {
2480
1044
  if (!transportRef.current) return;
2481
1045
  if (ref.current.busy) {
@@ -2490,7 +1054,8 @@ function usePilot(transport, opts = {}) {
2490
1054
  // eslint-disable-next-line react-hooks/exhaustive-deps
2491
1055
  [commit, runNow]
2492
1056
  );
2493
- const sendNew = useCallback2(
1057
+ sendRef.current = (text) => send(text);
1058
+ const sendNew = useCallback(
2494
1059
  async (text, attachments, tries = 0) => {
2495
1060
  const resume = pendingResume();
2496
1061
  answers.current.clear();
@@ -2517,7 +1082,7 @@ function usePilot(transport, opts = {}) {
2517
1082
  const attachments = queued.flatMap((q) => q.attachments ?? []);
2518
1083
  void sendNew(text, attachments, Math.max(...queued.map((q) => q.tries)));
2519
1084
  };
2520
- const stop = useCallback2(() => {
1085
+ const stop = useCallback(() => {
2521
1086
  const run = liveRun.current ?? (ref.current.busy ? ref.current.runId : null);
2522
1087
  liveRun.current = null;
2523
1088
  ctrl.current?.abort();
@@ -2530,14 +1095,14 @@ function usePilot(transport, opts = {}) {
2530
1095
  }).catch(() => {
2531
1096
  });
2532
1097
  }, [commit, threadId]);
2533
- const retry = useCallback2(() => {
1098
+ const retry = useCallback(() => {
2534
1099
  const s = ref.current;
2535
1100
  const last = lastUserTurn(s.turns);
2536
1101
  if (!last || s.busy) return;
2537
1102
  commit(dropLastExchange(s));
2538
1103
  void send(last.text, last.attachments ?? []);
2539
1104
  }, [commit, send]);
2540
- const sendAnswersIfComplete = useCallback2(() => {
1105
+ const sendAnswersIfComplete = useCallback(() => {
2541
1106
  const open = ref.current.permissions.filter((p) => !answers.current.has(p.id));
2542
1107
  if (open.length || !answers.current.size || ref.current.busy) return;
2543
1108
  const resume = [...answers.current.entries()].map(([interruptId, a]) => ({ interruptId, ...a }));
@@ -2545,18 +1110,18 @@ function usePilot(transport, opts = {}) {
2545
1110
  commit(beginSend(ref.current, "", []));
2546
1111
  void runNow({ messages: [], resume });
2547
1112
  }, [commit, runNow]);
2548
- const respondPermission = useCallback2(
1113
+ const respondPermission = useCallback(
2549
1114
  (id, approved) => {
2550
1115
  answers.current.set(id, { status: "resolved", payload: { approved } });
2551
1116
  sendAnswersIfComplete();
2552
1117
  },
2553
1118
  [sendAnswersIfComplete]
2554
1119
  );
2555
- const recordDecision = useCallback2(
1120
+ const recordDecision = useCallback(
2556
1121
  (id, status, asked) => commit(resolvePermission(ref.current, id, status, asked)),
2557
1122
  [commit]
2558
1123
  );
2559
- const expirePermission = useCallback2(
1124
+ const expirePermission = useCallback(
2560
1125
  (id) => {
2561
1126
  if (!ref.current.permissions.some((p) => p.id === id)) return;
2562
1127
  answers.current.set(id, { status: "cancelled" });
@@ -2565,9 +1130,9 @@ function usePilot(transport, opts = {}) {
2565
1130
  },
2566
1131
  [commit, sendAnswersIfComplete]
2567
1132
  );
2568
- const [loadingOlder, setLoadingOlder] = useState8(false);
2569
- const loadingRef = useRef4(false);
2570
- const loadOlder = useCallback2(async () => {
1133
+ const [loadingOlder, setLoadingOlder] = useState7(false);
1134
+ const loadingRef = useRef3(false);
1135
+ const loadOlder = useCallback(async () => {
2571
1136
  const fn = optsRef.current.loadOlder;
2572
1137
  const h = ref.current.history;
2573
1138
  if (!fn || !h?.hasMoreBefore || loadingRef.current) return;
@@ -2584,7 +1149,7 @@ function usePilot(transport, opts = {}) {
2584
1149
  setLoadingOlder(false);
2585
1150
  }
2586
1151
  }, [commit]);
2587
- const stopSubagent = useCallback2(
1152
+ const stopSubagent = useCallback(
2588
1153
  async (taskId) => {
2589
1154
  const t = transportRef.current;
2590
1155
  if (!t) return;
@@ -2598,72 +1163,76 @@ function usePilot(transport, opts = {}) {
2598
1163
  // eslint-disable-next-line react-hooks/exhaustive-deps
2599
1164
  [commit, runNow]
2600
1165
  );
2601
- const continueRun = useCallback2(async () => {
1166
+ const continueRun = useCallback(async () => {
2602
1167
  const cut = ref.current.cutRun;
2603
1168
  if (!cut || ref.current.busy || !transportRef.current) return;
2604
1169
  commit(beginSend(ref.current, "", []));
2605
1170
  await runNow({ messages: [], forwardedProps: { continueRun: cut.runId } });
2606
1171
  }, [commit, runNow]);
2607
- const refresh = useCallback2(() => runNow({ messages: [] }, true), [runNow]);
1172
+ const refresh = useCallback(() => runNow({ messages: [] }, true), [runNow]);
2608
1173
  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 };
1174
+ const subagents = useMemo2(() => subagentsOf(state.agentState), [state.agentState]);
1175
+ return { state, threadId, send, stop, retry, respondPermission, recordDecision, expirePermission, canSteer, loadOlder, loadingOlder, subagents, stopSubagent, refresh, reconnect: refresh, continueRun, subscribe, onTurn };
2611
1176
  }
2612
1177
 
2613
1178
  // 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;
1179
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1180
+ var useIsoLayoutEffect = typeof window === "undefined" ? useEffect5 : useLayoutEffect;
2616
1181
  var ANCHOR_GAP = 24;
2617
- var Piloto = forwardRef3(function Piloto2(props, handle) {
2618
- const { transport, pilot: external, locale = "es", theme = "light", features = {}, slots = {} } = props;
1182
+ var Piloto = forwardRef(function Piloto2(props, handle) {
1183
+ const { transport, pilot: external, locale = "es", theme = "light", features = {} } = props;
2619
1184
  const own = usePilot(external ? null : transport, features.steer === void 0 ? props : { ...props, steer: features.steer });
2620
1185
  const pilot = external ?? own;
2621
1186
  const { state } = pilot;
2622
1187
  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(
1188
+ const plugins = useMemo3(() => resolvePlugins(props.plugins), [props.plugins]);
1189
+ useEffect5(() => {
1190
+ if (isDev()) for (const w of plugins.warnings) warnOnce(w);
1191
+ }, [plugins]);
1192
+ const strings = useMemo3(() => ({ ...locale === "en" ? STRINGS_EN : STRINGS_ES, ...plugins.strings }), [locale, plugins]);
1193
+ const config = useMemo3(
2625
1194
  () => ({
2626
1195
  strings,
2627
- toolVerbs: { ...locale === "en" ? TOOL_VERBS_EN : TOOL_VERBS_ES, ...props.toolVerbs },
2628
- toolIcons: props.toolIcons ?? {},
2629
- formatUserText: props.formatUserText ?? cleanUserText,
1196
+ toolVerbs: { ...locale === "en" ? TOOL_VERBS_EN : TOOL_VERBS_ES, ...plugins.verbs },
1197
+ toolIcons: plugins.icons,
1198
+ formatUserText: plugins.formatUserText ? (x) => plugins.formatUserText(cleanUserText(x)) : cleanUserText,
2630
1199
  theme,
2631
1200
  skin: props.skin ?? "ghosty",
2632
- markdown: props.markdown ?? {}
1201
+ markdown: plugins.markdown
2633
1202
  }),
2634
- [strings, locale, props.toolVerbs, props.toolIcons, props.formatUserText, theme, props.skin, props.markdown]
1203
+ [strings, locale, plugins, theme, props.skin]
2635
1204
  );
2636
1205
  const t = strings;
2637
- const [ownRefs, setOwnRefs] = useState9([]);
1206
+ const [ownRefs, setOwnRefs] = useState8([]);
2638
1207
  const refs = props.references ?? ownRefs;
2639
- const refsRef = useRef5(refs);
1208
+ const refsRef = useRef4(refs);
2640
1209
  refsRef.current = refs;
2641
- const setRefs = useCallback3(
1210
+ const setRefs = useCallback2(
2642
1211
  (next) => {
2643
1212
  if (!props.references) setOwnRefs(next);
2644
1213
  props.onReferencesChange?.(next);
2645
1214
  },
2646
1215
  [props.references, props.onReferencesChange]
2647
1216
  );
2648
- const composerBox = useRef5(null);
1217
+ const composerBox = useRef4(null);
2649
1218
  const draftText = typeof props.draft === "string" ? props.draft : props.draft?.text;
2650
1219
  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(() => {
1220
+ const [draft, setDraftState] = useState8(() => ({ text: draftText ?? "", n: 0 }));
1221
+ const draftSeen = useRef4(`${draftText}\0${draftKey}`);
1222
+ useEffect5(() => {
2654
1223
  const id = `${draftText}\0${draftKey}`;
2655
1224
  if (draftText == null || id === draftSeen.current) return;
2656
1225
  draftSeen.current = id;
2657
1226
  setDraftState((d) => ({ text: draftText, n: d.n + 1 }));
2658
1227
  }, [draftText, draftKey]);
2659
1228
  const returned = state.returnedDraft;
2660
- useEffect6(() => {
1229
+ useEffect5(() => {
2661
1230
  if (!returned) return;
2662
1231
  const typed = composerBox.current?.querySelector("textarea")?.value ?? "";
2663
1232
  const text = [returned.text, typed].filter((x) => x.trim()).join("\n\n");
2664
1233
  setDraftState((d) => ({ text, n: d.n + 1 }));
2665
1234
  }, [returned?.n]);
2666
- const send = useCallback3(
1235
+ const send = useCallback2(
2667
1236
  (text, attachments = []) => {
2668
1237
  const current = refsRef.current;
2669
1238
  const full = applyReferences(text, current);
@@ -2672,22 +1241,82 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2672
1241
  },
2673
1242
  [pilot, setRefs]
2674
1243
  );
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 })
1244
+ const composerHandle = useRef4(null);
1245
+ const focus = useCallback2(() => composerBox.current?.querySelector("textarea")?.focus({ preventScroll: true }), []);
1246
+ const addReference = useCallback2(
1247
+ (r) => {
1248
+ const current = refsRef.current;
1249
+ if (!current.some((x) => x.id === r.id)) setRefs([...current, r]);
1250
+ requestAnimationFrame(focus);
1251
+ },
1252
+ [setRefs, focus]
1253
+ );
1254
+ const setDraft = useCallback2((text) => setDraftState((d) => ({ text, n: d.n + 1 })), []);
1255
+ const addAttachments = useCallback2((items) => composerHandle.current?.addAttachments(items), []);
1256
+ const actions = { pilot, send, addReference, addAttachments, setDraft, focus };
1257
+ useImperativeHandle(handle, () => actions, [pilot, send, addReference, addAttachments, setDraft, focus]);
1258
+ const ctx = { ...actions, agentState: state.agentState, strings, plugins: plugins.list };
1259
+ const ctxRef = useRef4(ctx);
1260
+ ctxRef.current = ctx;
1261
+ const pluginsRef = useRef4(plugins);
1262
+ pluginsRef.current = plugins;
1263
+ const unattended = useRef4(/* @__PURE__ */ new Set());
1264
+ useEffect5(
1265
+ () => pilot.subscribe?.((event) => {
1266
+ const e = event;
1267
+ const r = pluginsRef.current;
1268
+ const handlers = [...r.events[e.type] ?? [], ...e.type === "CUSTOM" && e.name ? r.events[e.name] ?? [] : [], ...r.events["*"] ?? []];
1269
+ for (const h of handlers) {
1270
+ try {
1271
+ h.value(e, ctxRef.current);
1272
+ } catch (err) {
1273
+ console.error(`[ghostykit] el plugin \xAB${h.plugin}\xBB fall\xF3 con ${e.type}${e.name ? ` ${e.name}` : ""}`, err);
1274
+ }
1275
+ }
1276
+ 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)) {
1277
+ const declared = r.list.some((p) => p.requires?.events?.includes(e.name));
1278
+ if (!declared) {
1279
+ unattended.current.add(e.name);
1280
+ warnOnce(`Ning\xFAn plugin atiende CUSTOM \xAB${e.name}\xBB.`);
1281
+ }
1282
+ }
2686
1283
  }),
2687
- [send, setRefs]
1284
+ [pilot.subscribe]
2688
1285
  );
2689
- const [held, setHeld] = useState9({});
2690
- const heldRef = useRef5(held);
1286
+ const seenState = useRef4({});
1287
+ useEffect5(() => {
1288
+ const r = pluginsRef.current;
1289
+ for (const [key, handlers] of Object.entries(r.state)) {
1290
+ const value = state.agentState[key];
1291
+ if (value === seenState.current[key]) continue;
1292
+ seenState.current[key] = value;
1293
+ if (value === void 0) continue;
1294
+ for (const h of handlers) {
1295
+ try {
1296
+ h.value(value, ctxRef.current);
1297
+ } catch (err) {
1298
+ console.error(`[ghostykit] el plugin \xAB${h.plugin}\xBB fall\xF3 con state.${key}`, err);
1299
+ }
1300
+ }
1301
+ }
1302
+ }, [state.agentState, plugins]);
1303
+ useEffect5(
1304
+ () => pilot.onTurn?.((e) => {
1305
+ const r = pluginsRef.current;
1306
+ const run = (plugin, fn) => {
1307
+ try {
1308
+ fn();
1309
+ } catch (err) {
1310
+ console.error(`[ghostykit] el plugin \xAB${plugin}\xBB fall\xF3 al ${e.type === "start" ? "empezar" : "terminar"} el turno`, err);
1311
+ }
1312
+ };
1313
+ if (e.type === "start") for (const h of r.turnStart) run(h.plugin, () => h.value(ctxRef.current));
1314
+ else for (const h of r.turnDone) run(h.plugin, () => h.value(e.info, ctxRef.current));
1315
+ }),
1316
+ [pilot.onTurn]
1317
+ );
1318
+ const [held, setHeld] = useState8({});
1319
+ const heldRef = useRef4(held);
2691
1320
  heldRef.current = held;
2692
1321
  const releaseHeld = (id) => setHeld(({ [id]: _, ...rest }) => rest);
2693
1322
  const respond = (id, approved) => {
@@ -2703,15 +1332,15 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2703
1332
  releaseHeld(id);
2704
1333
  };
2705
1334
  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);
1335
+ const scroller = useRef4(null);
1336
+ const column = useRef4(null);
1337
+ const spacer = useRef4(null);
1338
+ const following = useRef4(true);
1339
+ const touchY = useRef4(null);
1340
+ const [atBottom, setAtBottom] = useState8(true);
2712
1341
  const message = (n) => scroller.current?.querySelector(`[data-turn="${n}"]`) ?? null;
2713
- const olderSeen = useRef5(olderCount);
2714
- const heightBefore = useRef5(0);
1342
+ const olderSeen = useRef4(olderCount);
1343
+ const heightBefore = useRef4(0);
2715
1344
  useIsoLayoutEffect(() => {
2716
1345
  const el = scroller.current;
2717
1346
  if (el && olderCount > olderSeen.current && heightBefore.current) el.scrollTop += el.scrollHeight - heightBefore.current;
@@ -2722,8 +1351,8 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2722
1351
  heightBefore.current = scroller.current?.scrollHeight ?? 0;
2723
1352
  void pilot.loadOlder();
2724
1353
  };
2725
- const placed = useRef5(false);
2726
- useEffect6(() => {
1354
+ const placed = useRef4(false);
1355
+ useEffect5(() => {
2727
1356
  if (placed.current || turns.length === 0) return;
2728
1357
  placed.current = true;
2729
1358
  const el = scroller.current;
@@ -2738,16 +1367,20 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2738
1367
  }
2739
1368
  }
2740
1369
  const mine = mineIndex >= 0 ? turns[mineIndex] : null;
2741
- const mineSeen = useRef5(mine);
2742
- const [anchor, setAnchor] = useState9(null);
2743
- useEffect6(() => {
1370
+ const mineSeen = useRef4(mine);
1371
+ const [anchor, setAnchor] = useState8(null);
1372
+ useEffect5(() => {
2744
1373
  if (mine === mineSeen.current) return;
2745
1374
  mineSeen.current = mine;
1375
+ if (!state.turnStartedAt) {
1376
+ following.current = true;
1377
+ return;
1378
+ }
2746
1379
  if (mine) setAnchor(mineIndex - olderCount);
2747
1380
  following.current = true;
2748
1381
  }, [mine, mineIndex]);
2749
1382
  const gapAbove = (a) => anchor === -olderCount ? a.offsetTop : ANCHOR_GAP;
2750
- const measureSpacer = useCallback3(() => {
1383
+ const measureSpacer = useCallback2(() => {
2751
1384
  const sc = scroller.current;
2752
1385
  const sp = spacer.current;
2753
1386
  if (!sc || !sp) return;
@@ -2765,7 +1398,7 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2765
1398
  const a = anchor != null ? message(anchor) : null;
2766
1399
  if (sc && a) sc.scrollTo({ top: a.offsetTop - gapAbove(a), behavior: "smooth" });
2767
1400
  }, [anchor, measureSpacer]);
2768
- useEffect6(() => {
1401
+ useEffect5(() => {
2769
1402
  const col = column.current;
2770
1403
  const sc = scroller.current;
2771
1404
  if (!col || !sc || typeof ResizeObserver === "undefined") return;
@@ -2795,13 +1428,14 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2795
1428
  const toolRunning = busy && last?.role === "assistant" && (last.tools ?? []).some(isRunning);
2796
1429
  const empty = turns.length === 0 && !busy;
2797
1430
  const placeholder = props.placeholder ?? (props.agentName ? t.placeholder(props.agentName) : t.placeholderDefault);
2798
- const showFiles = features.attachments ?? Boolean(props.upload);
1431
+ const { upload, transcribe, voiceNoteText } = plugins.attachments;
1432
+ const showFiles = features.attachments ?? Boolean(upload);
2799
1433
  const pointedRefs = refs.filter((r) => r.prompt);
2800
1434
  const plainRefs = refs.filter((r) => !r.prompt);
2801
1435
  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(
1436
+ const composer = /* @__PURE__ */ jsxs6("div", { ref: composerBox, children: [
1437
+ /* @__PURE__ */ jsx8(ReferenceChips, { references: pointedRefs, onRemove: removeRef, onActivate: props.onReferenceClick }),
1438
+ /* @__PURE__ */ jsx8(
2805
1439
  Composer,
2806
1440
  {
2807
1441
  initialValue: draft.text,
@@ -2811,156 +1445,232 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
2811
1445
  canSteer: pilot.canSteer,
2812
1446
  placeholder,
2813
1447
  attachments: showFiles,
2814
- upload: props.upload,
2815
- transcribe: props.transcribe,
2816
- voiceNoteText: props.voiceNoteText,
1448
+ upload,
1449
+ transcribe,
1450
+ voiceNoteText,
2817
1451
  references: plainRefs,
2818
1452
  onRemoveReference: removeRef,
2819
1453
  onActivateReference: props.onReferenceClick,
2820
- extras: slots.composerExtras
1454
+ extras: plugins.composerActions.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.composerActions, ctx, styled: plugins.ghostykitStyled, inline: true }) : void 0,
1455
+ extrasEnd: plugins.composerActionsEnd.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.composerActionsEnd, ctx, styled: plugins.ghostykitStyled, inline: true }) : void 0,
1456
+ handleRef: composerHandle
2821
1457
  },
2822
1458
  draft.n
2823
1459
  )
2824
1460
  ] });
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)
1461
+ const withPanel = (chat) => plugins.sidePanel.length ? /* @__PURE__ */ jsxs6("div", { className: "flex min-h-0 flex-1 flex-row", children: [
1462
+ /* @__PURE__ */ jsx8("div", { className: "relative flex min-h-0 min-w-0 flex-1 flex-col", children: chat }),
1463
+ /* @__PURE__ */ jsx8(Views, { items: plugins.sidePanel, ctx, styled: plugins.ghostykitStyled, className: "flex min-h-0 shrink-0" })
2828
1464
  ] }) : 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(
1465
+ const aboveComposer = plugins.composerAbove.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.composerAbove, ctx, styled: plugins.ghostykitStyled }) : null;
1466
+ const belowComposer = plugins.composerBelow.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.composerBelow, ctx, styled: plugins.ghostykitStyled }) : null;
1467
+ const header = plugins.header.length ? /* @__PURE__ */ jsx8(Views, { items: plugins.header, ctx, styled: plugins.ghostykitStyled, inline: true }) : null;
1468
+ const mark = (plugin) => pluginAttrs(plugin, plugins.ghostykitStyled);
1469
+ const Empty = plugins.empty?.value;
1470
+ const Waiting = plugins.waiting?.value;
1471
+ const renderDeliveries = (turn, live) => {
1472
+ const custom = (turn.custom ?? []).map((entry, i) => {
1473
+ const owner = plugins.customRenders[entry.name];
1474
+ if (!owner) return null;
1475
+ const C = owner.value;
1476
+ return /* @__PURE__ */ jsx8("div", { ...mark(owner.plugin), children: /* @__PURE__ */ jsx8(C, { ctx, entry, turn, live }) }, `c${i}`);
1477
+ });
1478
+ const extras = plugins.messageAssistant.map((o, i) => /* @__PURE__ */ jsx8("div", { ...mark(o.plugin), children: /* @__PURE__ */ jsx8(o.value, { ctx, turn, live }) }, `a${i}`));
1479
+ return custom.some(Boolean) || extras.length ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
1480
+ custom,
1481
+ extras
1482
+ ] }) : null;
1483
+ };
1484
+ const renderTools = (turn, live) => {
1485
+ const views = (turn.tools ?? []).flatMap((tool) => {
1486
+ const owner = plugins.toolRenders[tool.title ?? ""] ?? (tool.kind ? plugins.toolRenders[tool.kind] : void 0);
1487
+ if (!owner) return [];
1488
+ const C = owner.value;
1489
+ return [
1490
+ /* @__PURE__ */ jsx8("div", { ...mark(owner.plugin), children: /* @__PURE__ */ jsx8(C, { ctx, tool, turn, live }) }, tool.id)
1491
+ ];
1492
+ });
1493
+ return views.length ? /* @__PURE__ */ jsx8("div", { className: "mb-3 flex flex-col gap-2", children: views }) : null;
1494
+ };
1495
+ 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;
1496
+ const blockLangs = Object.keys(plugins.blockRenders);
1497
+ const blocks = blockLangs.length ? {
1498
+ langs: blockLangs,
1499
+ render: (list, turn, live) => /* @__PURE__ */ jsx8("div", { className: "mt-2 flex flex-col gap-2", children: list.map((block, i) => {
1500
+ const owner = plugins.blockRenders[block.lang];
1501
+ const C = owner.value;
1502
+ return /* @__PURE__ */ jsx8("div", { ...mark(owner.plugin), children: /* @__PURE__ */ jsx8(C, { ctx, block, turn, live }) }, i);
1503
+ }) })
1504
+ } : void 0;
1505
+ const wrapAll = (tree) => plugins.wraps.reduceRight((inner, { value: W }) => /* @__PURE__ */ jsx8(W, { ctx, children: inner }), tree);
1506
+ const permissionCards = cards.map((p) => /* @__PURE__ */ jsx8(ConfirmCard, { request: p, onRespond: respond, onSettled: settle, onExpire: pilot.expirePermission }, p.id));
1507
+ return /* @__PURE__ */ jsx8(PilotoContext.Provider, { value: config, children: wrapAll(
1508
+ /* @__PURE__ */ jsx8(
2856
1509
  "div",
2857
1510
  {
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(
1511
+ ...rootProps(theme, props.skin),
1512
+ "data-busy": busy || void 0,
1513
+ className: `piloto ${plugins.className} ${props.className ?? ""}`,
1514
+ style: Object.keys(plugins.vars).length ? { ...plugins.vars, ...props.style } : props.style,
1515
+ children: withPanel(empty ? (
1516
+ // Un hilo vacío enseña lo mismo que la portada: la cara, el saludo, la caja y por dónde
1517
+ // empezar. Si no cabe (panel bajo), se desplaza: `safe center` nunca empuja nada fuera.
1518
+ /* @__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: [
1519
+ header && /* @__PURE__ */ jsx8("div", { className: "absolute right-4 top-3 flex gap-2", children: header }),
1520
+ 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: [
1521
+ props.avatar && /* @__PURE__ */ jsx8("div", { className: "mb-3", children: props.avatar }),
1522
+ props.welcome && // Un saludo largo (una explicación del piloto) baja a letra de lectura.
1523
+ /* @__PURE__ */ jsx8(
1524
+ "h1",
1525
+ {
1526
+ 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",
1527
+ children: props.welcome
1528
+ }
1529
+ )
1530
+ ] }),
1531
+ /* @__PURE__ */ jsxs6("div", { className: "w-full max-w-3xl", children: [
1532
+ aboveComposer,
1533
+ composer,
1534
+ belowComposer,
1535
+ props.suggestions && /* @__PURE__ */ jsx8(Suggestions, { items: props.suggestions, onPick: (s) => send(s), onPrefill: (s) => setDraftState((d) => ({ text: s, n: d.n + 1 })) })
1536
+ ] })
1537
+ ] })
1538
+ ) : /* @__PURE__ */ jsxs6(Fragment4, { children: [
1539
+ header && /* @__PURE__ */ jsx8("div", { className: "flex shrink-0 justify-end gap-2 px-4 pt-3 sm:px-6", children: header }),
1540
+ /* @__PURE__ */ jsxs6(
1541
+ "div",
1542
+ {
1543
+ ref: scroller,
1544
+ onScroll,
1545
+ onWheel: (e) => e.deltaY < 0 && release(),
1546
+ onTouchStart: (e) => {
1547
+ touchY.current = e.touches[0]?.clientY ?? null;
1548
+ },
1549
+ onTouchMove: (e) => {
1550
+ const y = e.touches[0]?.clientY;
1551
+ if (touchY.current != null && y != null && y > touchY.current + 4) release();
1552
+ },
1553
+ onKeyDown: (e) => ["ArrowUp", "PageUp", "Home"].includes(e.key) && release(),
1554
+ className: "gs-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-hidden",
1555
+ children: [
1556
+ /* @__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: [
1557
+ plugins.aboveThread.length > 0 && /* @__PURE__ */ jsx8(Views, { items: plugins.aboveThread, ctx, styled: plugins.ghostykitStyled }),
1558
+ state.history?.hasMoreBefore && (external || props.loadOlder) && /* @__PURE__ */ jsx8(
1559
+ "button",
1560
+ {
1561
+ type: "button",
1562
+ onClick: loadOlder,
1563
+ disabled: pilot.loadingOlder,
1564
+ 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",
1565
+ children: pilot.loadingOlder ? t.loadingOlder : t.loadOlder
1566
+ }
1567
+ ),
1568
+ turns.map((turn, i) => {
1569
+ const isLast = i === turns.length - 1;
1570
+ const live = busy && isLast;
1571
+ return /* @__PURE__ */ jsxs6("div", { "data-turn": i - olderCount, children: [
1572
+ turn.role === "user" ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
1573
+ /* @__PURE__ */ jsx8(UserBubble, { turn }),
1574
+ plugins.messageUser.map((o, j) => /* @__PURE__ */ jsx8("div", { ...mark(o.plugin), children: /* @__PURE__ */ jsx8(o.value, { ctx, turn }) }, j))
1575
+ ] }) : /* @__PURE__ */ jsx8(
1576
+ AgentBubble,
1577
+ {
1578
+ turn,
1579
+ live,
1580
+ onRetry: isLast && features.retry !== false ? pilot.retry : void 0,
1581
+ onOption: isLast && !busy && features.replyOptions !== false ? (text) => send(text) : void 0,
1582
+ showUsage: features.usage !== false,
1583
+ showCopy: features.copy !== false,
1584
+ showDuration: features.duration !== false,
1585
+ renderDeliveries,
1586
+ renderTools,
1587
+ renderBefore,
1588
+ blocks
1589
+ }
1590
+ ),
1591
+ turn.role === "assistant" && /* @__PURE__ */ jsx8(TurnNotes, { turn })
1592
+ ] }, i - olderCount);
1593
+ }),
1594
+ busy && state.stalled && /* @__PURE__ */ jsx8("p", { className: "text-sm text-text-tertiary", children: state.stalled.message ?? t.stalled }),
1595
+ state.queued.map((q, i) => (
1596
+ // Esperan a que termine el run vivo (el servidor no los dejó entrar): salen solos.
1597
+ /* @__PURE__ */ jsxs6("div", { "data-queued": true, className: "flex flex-col items-end gap-1 opacity-60", children: [
1598
+ /* @__PURE__ */ jsx8(UserBubble, { turn: { role: "user", text: q.text, ...q.attachments ? { attachments: q.attachments } : {} } }),
1599
+ /* @__PURE__ */ jsx8("span", { className: "text-xs text-text-tertiary", children: t.queued })
1600
+ ] }, `q${i}`)
1601
+ )),
1602
+ permissionCards,
1603
+ 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: [
1604
+ /* @__PURE__ */ jsx8(TypingDots, {}),
1605
+ /* @__PURE__ */ jsx8(WaitLabel, { since: state.turnStartedAt, step: state.step, showDuration: features.duration !== false })
1606
+ ] })),
1607
+ notice && /* @__PURE__ */ jsx8("p", { className: "text-sm text-text-tertiary", children: notice }),
1608
+ error && /* @__PURE__ */ jsx8("p", { className: "text-sm text-text-danger", children: error })
1609
+ ] }),
1610
+ /* @__PURE__ */ jsx8("div", { ref: spacer, "aria-hidden": true })
1611
+ ]
1612
+ }
1613
+ ),
1614
+ /* @__PURE__ */ jsxs6("div", { className: "relative mx-auto w-full max-w-[784px] px-4 pb-4 sm:px-6 sm:pb-6", children: [
1615
+ !atBottom && /* @__PURE__ */ jsx8(
2873
1616
  "button",
2874
1617
  {
2875
1618
  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
1619
+ onClick: toBottom,
1620
+ "aria-label": t.goToLatest,
1621
+ 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",
1622
+ children: /* @__PURE__ */ jsx8(ArrowDown, { className: "h-4 w-4" })
2880
1623
  }
2881
1624
  ),
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 })
1625
+ state.cutRun && !busy && // El servidor cortó la respuesta (deploy, caja caída): la persona decide si sigue.
1626
+ /* @__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: [
1627
+ /* @__PURE__ */ jsx8("span", { className: "min-w-0 flex-1", children: state.cutRun.reason ?? t.runCut }),
1628
+ /* @__PURE__ */ jsx8(
1629
+ "button",
1630
+ {
1631
+ type: "button",
1632
+ onClick: () => void pilot.continueRun(),
1633
+ className: "shrink-0 rounded-full bg-brand-500 px-3 py-1 text-sm font-medium text-white transition hover:bg-brand-600",
1634
+ children: t.continueRun
1635
+ }
1636
+ )
2914
1637
  ] }),
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
- ]
1638
+ aboveComposer,
1639
+ composer,
1640
+ belowComposer
1641
+ ] })
1642
+ ] }))
2920
1643
  }
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
- ] })) }) });
1644
+ )
1645
+ ) });
2950
1646
  });
1647
+ var warned = /* @__PURE__ */ new Set();
1648
+ function warnOnce(message) {
1649
+ if (warned.has(message)) return;
1650
+ warned.add(message);
1651
+ console.warn(`[ghostykit] ${message}`);
1652
+ }
1653
+ function pluginAttrs(plugin, styled) {
1654
+ return styled.has(plugin) ? { "data-plugin": plugin } : { "data-plugin": plugin, "data-piloto-isolate": "" };
1655
+ }
1656
+ function Views({ items, ctx, inline, className, styled }) {
1657
+ return /* @__PURE__ */ jsx8(Fragment4, { children: items.map(
1658
+ ({ 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)
1659
+ ) });
1660
+ }
2951
1661
  function TurnNotes({ turn }) {
2952
1662
  const { strings: t } = usePiloto();
2953
1663
  const failed = (turn.custom ?? []).filter((c) => c.name === "ghosty.attachments_failed").flatMap((c) => (c.value?.files ?? []).filter((f) => f?.name));
2954
1664
  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 })
1665
+ return /* @__PURE__ */ jsxs6("div", { "data-turn-notes": true, className: "mt-1 flex flex-col gap-0.5 text-xs text-text-tertiary", children: [
1666
+ failed.map((f, i) => /* @__PURE__ */ jsx8("p", { children: t.attachmentFailed(f.name, f.reason) }, i)),
1667
+ turn.truncated && /* @__PURE__ */ jsx8("p", { children: turn.truncated.reason ?? t.truncated })
2958
1668
  ] });
2959
1669
  }
2960
1670
 
2961
1671
  // src/react/Subagents.tsx
2962
- import { useState as useState10 } from "react";
2963
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1672
+ import { useState as useState9 } from "react";
1673
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2964
1674
  var DOT = {
2965
1675
  running: "bg-brand-500 animate-pulse",
2966
1676
  completed: "bg-text-tertiary",
@@ -2969,33 +1679,33 @@ var DOT = {
2969
1679
  };
2970
1680
  function Subagents({ pilot, defaultOpen = false, showFinished = true, className = "" }) {
2971
1681
  const { strings: t } = usePiloto();
2972
- const [open, setOpen] = useState10(defaultOpen);
1682
+ const [open, setOpen] = useState9(defaultOpen);
2973
1683
  const all = pilot.subagents;
2974
1684
  const tasks = showFinished ? all : all.filter((x) => x.status === "running");
2975
1685
  if (!tasks.length) return null;
2976
1686
  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" })
1687
+ return /* @__PURE__ */ jsxs7("div", { "data-subagents": true, className: `mb-2 rounded-2xl border border-border-primary bg-background-secondary text-sm ${className}`, children: [
1688
+ /* @__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: [
1689
+ /* @__PURE__ */ jsx9("span", { className: `h-2 w-2 shrink-0 rounded-full ${running ? DOT.running : DOT.completed}` }),
1690
+ /* @__PURE__ */ jsx9("span", { className: "flex-1 truncate", children: running ? t.subagentsWorking(running) : t.subagentsDone(tasks.length) }),
1691
+ /* @__PURE__ */ jsx9("span", { "aria-hidden": true, className: `text-text-tertiary transition-transform ${open ? "rotate-180" : ""}`, children: "\u2304" })
2982
1692
  ] }),
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)) })
1693
+ 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
1694
  ] });
2985
1695
  }
2986
1696
  function SubagentRow({ task, onStop }) {
2987
1697
  const { strings: t } = usePiloto();
2988
1698
  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: [
1699
+ return /* @__PURE__ */ jsxs7("li", { "data-subagent": task.id, "data-status": task.status, className: "flex items-center gap-2 px-3 py-2", children: [
1700
+ /* @__PURE__ */ jsx9("span", { className: `h-2 w-2 shrink-0 rounded-full ${DOT[task.status] ?? DOT.completed}` }),
1701
+ /* @__PURE__ */ jsxs7("div", { className: "min-w-0 flex-1", children: [
1702
+ /* @__PURE__ */ jsx9("div", { className: "truncate text-text-primary", children: task.title ?? task.id }),
1703
+ /* @__PURE__ */ jsxs7("div", { className: "truncate text-xs text-text-tertiary", children: [
2994
1704
  t.subagentStatus[task.status] ?? task.status,
2995
1705
  line ? ` \xB7 ${line}` : ""
2996
1706
  ] })
2997
1707
  ] }),
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 })
1708
+ 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
1709
  ] });
3000
1710
  }
3001
1711
  export {
@@ -3041,8 +1751,10 @@ export {
3041
1751
  createAgUiTransport,
3042
1752
  createPilotState,
3043
1753
  Piloto as default,
1754
+ definePlugin,
3044
1755
  dropLastExchange,
3045
1756
  expiryMs,
1757
+ extractBlocks,
3046
1758
  extractReplyOptions,
3047
1759
  formatDuration,
3048
1760
  groupSteps,
@@ -3053,12 +1765,15 @@ export {
3053
1765
  messagesToTurns,
3054
1766
  mimeOf,
3055
1767
  narrationToMarkdown,
1768
+ pluginPoints,
1769
+ pluginRequires,
3056
1770
  prependHistory,
3057
1771
  queueMessage,
3058
1772
  readSse,
3059
1773
  reduceAgUiEvent,
3060
1774
  renderInline,
3061
1775
  resolvePermission,
1776
+ resolvePlugins,
3062
1777
  sendFailed,
3063
1778
  splitNarration,
3064
1779
  stepLabel,