@webskill/chatbot 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,10 +6,10 @@ import { t as yaml } from "./yaml-C888tCbJ.js";
6
6
  import { t as json } from "./json-BOcU6nei.js";
7
7
  import { t as typescript } from "./typescript-DAau_Qyl.js";
8
8
  import { t as jsx$1 } from "./jsx-D2Jv0wvt.js";
9
- import { AnthropicClient, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FullDisclosureRouter, GoogleGenAiClient, OpenAiCompatibleClient, SkillDiscovery, WebSkillError, WebSkillRuntime, messageOf } from "@webskill/sdk";
9
+ import { AnthropicClient, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FullDisclosureRouter, GoogleGenAiClient, OpenAiCompatibleClient, SkillDiscovery, WebSkillError, WebSkillRuntime, isUnsupportedRunSnapshot, messageOf, summarizeToolCalls, textParts } from "@webskill/sdk";
10
10
  import { BrowserWorkerScriptExecutor } from "@webskill/sdk/browser";
11
- import { JsonRenderSpecSurface, NativeSpecSurface, OpenUiSpecSurface, ReactBridgeState, UiSurfaceList, UiSurfaceSnapshotList } from "@webskill/sdk/ui-react";
12
- import { A2UI_SPEC_FORM_PATH, LitRendererBridge, OPENUI_SURFACE_ACTION, VERCEL_SURFACE_ACTION_DATA_PART_TYPE, collectValues, createUiCatalogToolSource, fromA2uiSpecAction, fromA2uiSurfaceAction, fromOpenUiAction, fromOpenUiSurfaceAction, fromVercelSurfaceAction, interactionToFormModel, isUiSpecSurface, loadOpenUiPeers, loadWebSkillLitCatalog, renderMiniChart, shapeInteractionValue, toA2uiSpecMessages, toA2uiSurfaceAction, toA2uiSurfaceMessages, toOpenUiLang, toOpenUiSurfaceLang, toVercelSurfaceDataPart, toVercelToolInvocation, uiCatalog } from "@webskill/sdk/ui";
11
+ import { JsonRenderSpecSurface, NativeSpecSurface, OpenUiSpecSurface, ReactBridgeState, SpecInteractionChannel, UiSurfaceList, UiSurfaceSnapshotList, probeJsonRenderAvailability } from "@webskill/sdk/ui-react";
12
+ import { A2UI_SPEC_FORM_PATH, createUiCatalogToolSource, fromA2uiSpecAction, fromA2uiSurfaceAction, interactionToFormModel, interactionToUiSpec, loadOpenUiPeers, loadWebSkillLitCatalog, renderMiniChart, shapeInteractionValue, toA2uiSpecMessages, toA2uiSurfaceAction, toVercelToolInvocation, uiCatalog } from "@webskill/sdk/ui";
13
13
  import * as React$1 from "react";
14
14
  import React, { Children, Component, Fragment, createContext, createElement, forwardRef, isValidElement, memo, useCallback, useContext, useDeferredValue, useEffect, useEffectEvent, useId, useInsertionEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
15
15
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -183,6 +183,70 @@ const createLucideIcon = (iconName, iconNode) => {
183
183
  return Component;
184
184
  };
185
185
 
186
+ //#endregion
187
+ //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/archive-restore.mjs
188
+ /**
189
+ * @license lucide-react v1.27.0 - ISC
190
+ *
191
+ * This source code is licensed under the ISC license.
192
+ * See the LICENSE file in the root directory of this source tree.
193
+ */
194
+ const __iconNode$33 = [
195
+ ["rect", {
196
+ width: "20",
197
+ height: "5",
198
+ x: "2",
199
+ y: "3",
200
+ rx: "1",
201
+ key: "1wp1u1"
202
+ }],
203
+ ["path", {
204
+ d: "M4 8v11a2 2 0 0 0 2 2h2",
205
+ key: "tvwodi"
206
+ }],
207
+ ["path", {
208
+ d: "M20 8v11a2 2 0 0 1-2 2h-2",
209
+ key: "1gkqxj"
210
+ }],
211
+ ["path", {
212
+ d: "m9 15 3-3 3 3",
213
+ key: "1pd0qc"
214
+ }],
215
+ ["path", {
216
+ d: "M12 12v9",
217
+ key: "192myk"
218
+ }]
219
+ ];
220
+ const ArchiveRestore = createLucideIcon("archive-restore", __iconNode$33);
221
+
222
+ //#endregion
223
+ //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/archive.mjs
224
+ /**
225
+ * @license lucide-react v1.27.0 - ISC
226
+ *
227
+ * This source code is licensed under the ISC license.
228
+ * See the LICENSE file in the root directory of this source tree.
229
+ */
230
+ const __iconNode$32 = [
231
+ ["rect", {
232
+ width: "20",
233
+ height: "5",
234
+ x: "2",
235
+ y: "3",
236
+ rx: "1",
237
+ key: "1wp1u1"
238
+ }],
239
+ ["path", {
240
+ d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8",
241
+ key: "1s80jp"
242
+ }],
243
+ ["path", {
244
+ d: "M10 12h4",
245
+ key: "a56b0p"
246
+ }]
247
+ ];
248
+ const Archive = createLucideIcon("archive", __iconNode$32);
249
+
186
250
  //#endregion
187
251
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/arrow-up.mjs
188
252
  /**
@@ -191,14 +255,14 @@ const createLucideIcon = (iconName, iconNode) => {
191
255
  * This source code is licensed under the ISC license.
192
256
  * See the LICENSE file in the root directory of this source tree.
193
257
  */
194
- const __iconNode$30 = [["path", {
258
+ const __iconNode$31 = [["path", {
195
259
  d: "m5 12 7-7 7 7",
196
260
  key: "hav0vg"
197
261
  }], ["path", {
198
262
  d: "M12 19V5",
199
263
  key: "x0mq9r"
200
264
  }]];
201
- const ArrowUp = createLucideIcon("arrow-up", __iconNode$30);
265
+ const ArrowUp = createLucideIcon("arrow-up", __iconNode$31);
202
266
 
203
267
  //#endregion
204
268
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/check.mjs
@@ -208,11 +272,11 @@ const ArrowUp = createLucideIcon("arrow-up", __iconNode$30);
208
272
  * This source code is licensed under the ISC license.
209
273
  * See the LICENSE file in the root directory of this source tree.
210
274
  */
211
- const __iconNode$29 = [["path", {
275
+ const __iconNode$30 = [["path", {
212
276
  d: "M20 6 9 17l-5-5",
213
277
  key: "1gmf2c"
214
278
  }]];
215
- const Check = createLucideIcon("check", __iconNode$29);
279
+ const Check = createLucideIcon("check", __iconNode$30);
216
280
 
217
281
  //#endregion
218
282
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/chevron-down.mjs
@@ -222,11 +286,11 @@ const Check = createLucideIcon("check", __iconNode$29);
222
286
  * This source code is licensed under the ISC license.
223
287
  * See the LICENSE file in the root directory of this source tree.
224
288
  */
225
- const __iconNode$28 = [["path", {
289
+ const __iconNode$29 = [["path", {
226
290
  d: "m6 9 6 6 6-6",
227
291
  key: "qrunsl"
228
292
  }]];
229
- const ChevronDown = createLucideIcon("chevron-down", __iconNode$28);
293
+ const ChevronDown = createLucideIcon("chevron-down", __iconNode$29);
230
294
 
231
295
  //#endregion
232
296
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/circle-check.mjs
@@ -236,7 +300,7 @@ const ChevronDown = createLucideIcon("chevron-down", __iconNode$28);
236
300
  * This source code is licensed under the ISC license.
237
301
  * See the LICENSE file in the root directory of this source tree.
238
302
  */
239
- const __iconNode$27 = [["circle", {
303
+ const __iconNode$28 = [["circle", {
240
304
  cx: "12",
241
305
  cy: "12",
242
306
  r: "10",
@@ -245,7 +309,7 @@ const __iconNode$27 = [["circle", {
245
309
  d: "m9 12 2 2 4-4",
246
310
  key: "dzmm74"
247
311
  }]];
248
- const CircleCheck = createLucideIcon("circle-check", __iconNode$27);
312
+ const CircleCheck = createLucideIcon("circle-check", __iconNode$28);
249
313
 
250
314
  //#endregion
251
315
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/circle-question-mark.mjs
@@ -255,7 +319,7 @@ const CircleCheck = createLucideIcon("circle-check", __iconNode$27);
255
319
  * This source code is licensed under the ISC license.
256
320
  * See the LICENSE file in the root directory of this source tree.
257
321
  */
258
- const __iconNode$26 = [
322
+ const __iconNode$27 = [
259
323
  ["circle", {
260
324
  cx: "12",
261
325
  cy: "12",
@@ -271,7 +335,7 @@ const __iconNode$26 = [
271
335
  key: "p32p05"
272
336
  }]
273
337
  ];
274
- const CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode$26);
338
+ const CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode$27);
275
339
 
276
340
  //#endregion
277
341
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/circle-x.mjs
@@ -281,7 +345,7 @@ const CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode$2
281
345
  * This source code is licensed under the ISC license.
282
346
  * See the LICENSE file in the root directory of this source tree.
283
347
  */
284
- const __iconNode$25 = [
348
+ const __iconNode$26 = [
285
349
  ["circle", {
286
350
  cx: "12",
287
351
  cy: "12",
@@ -297,7 +361,7 @@ const __iconNode$25 = [
297
361
  key: "z0biqf"
298
362
  }]
299
363
  ];
300
- const CircleX = createLucideIcon("circle-x", __iconNode$25);
364
+ const CircleX = createLucideIcon("circle-x", __iconNode$26);
301
365
 
302
366
  //#endregion
303
367
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/clipboard-list.mjs
@@ -307,7 +371,7 @@ const CircleX = createLucideIcon("circle-x", __iconNode$25);
307
371
  * This source code is licensed under the ISC license.
308
372
  * See the LICENSE file in the root directory of this source tree.
309
373
  */
310
- const __iconNode$24 = [
374
+ const __iconNode$25 = [
311
375
  ["rect", {
312
376
  width: "8",
313
377
  height: "4",
@@ -338,7 +402,7 @@ const __iconNode$24 = [
338
402
  key: "18s6g9"
339
403
  }]
340
404
  ];
341
- const ClipboardList = createLucideIcon("clipboard-list", __iconNode$24);
405
+ const ClipboardList = createLucideIcon("clipboard-list", __iconNode$25);
342
406
 
343
407
  //#endregion
344
408
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/copy.mjs
@@ -348,7 +412,7 @@ const ClipboardList = createLucideIcon("clipboard-list", __iconNode$24);
348
412
  * This source code is licensed under the ISC license.
349
413
  * See the LICENSE file in the root directory of this source tree.
350
414
  */
351
- const __iconNode$23 = [["rect", {
415
+ const __iconNode$24 = [["rect", {
352
416
  width: "14",
353
417
  height: "14",
354
418
  x: "8",
@@ -360,7 +424,7 @@ const __iconNode$23 = [["rect", {
360
424
  d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",
361
425
  key: "zix9uf"
362
426
  }]];
363
- const Copy = createLucideIcon("copy", __iconNode$23);
427
+ const Copy = createLucideIcon("copy", __iconNode$24);
364
428
 
365
429
  //#endregion
366
430
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/download.mjs
@@ -370,7 +434,7 @@ const Copy = createLucideIcon("copy", __iconNode$23);
370
434
  * This source code is licensed under the ISC license.
371
435
  * See the LICENSE file in the root directory of this source tree.
372
436
  */
373
- const __iconNode$22 = [
437
+ const __iconNode$23 = [
374
438
  ["path", {
375
439
  d: "M12 15V3",
376
440
  key: "m9g1x1"
@@ -384,7 +448,7 @@ const __iconNode$22 = [
384
448
  key: "brsn70"
385
449
  }]
386
450
  ];
387
- const Download = createLucideIcon("download", __iconNode$22);
451
+ const Download = createLucideIcon("download", __iconNode$23);
388
452
 
389
453
  //#endregion
390
454
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/external-link.mjs
@@ -394,7 +458,7 @@ const Download = createLucideIcon("download", __iconNode$22);
394
458
  * This source code is licensed under the ISC license.
395
459
  * See the LICENSE file in the root directory of this source tree.
396
460
  */
397
- const __iconNode$21 = [
461
+ const __iconNode$22 = [
398
462
  ["path", {
399
463
  d: "M15 3h6v6",
400
464
  key: "1q9fwt"
@@ -408,7 +472,7 @@ const __iconNode$21 = [
408
472
  key: "a6xqqp"
409
473
  }]
410
474
  ];
411
- const ExternalLink = createLucideIcon("external-link", __iconNode$21);
475
+ const ExternalLink = createLucideIcon("external-link", __iconNode$22);
412
476
 
413
477
  //#endregion
414
478
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/file.mjs
@@ -418,14 +482,14 @@ const ExternalLink = createLucideIcon("external-link", __iconNode$21);
418
482
  * This source code is licensed under the ISC license.
419
483
  * See the LICENSE file in the root directory of this source tree.
420
484
  */
421
- const __iconNode$20 = [["path", {
485
+ const __iconNode$21 = [["path", {
422
486
  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",
423
487
  key: "1oefj6"
424
488
  }], ["path", {
425
489
  d: "M14 2v5a1 1 0 0 0 1 1h5",
426
490
  key: "wfsgrz"
427
491
  }]];
428
- const File = createLucideIcon("file", __iconNode$20);
492
+ const File = createLucideIcon("file", __iconNode$21);
429
493
 
430
494
  //#endregion
431
495
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/list-checks.mjs
@@ -435,7 +499,7 @@ const File = createLucideIcon("file", __iconNode$20);
435
499
  * This source code is licensed under the ISC license.
436
500
  * See the LICENSE file in the root directory of this source tree.
437
501
  */
438
- const __iconNode$19 = [
502
+ const __iconNode$20 = [
439
503
  ["path", {
440
504
  d: "M13 5h8",
441
505
  key: "a7qcls"
@@ -457,7 +521,7 @@ const __iconNode$19 = [
457
521
  key: "1obspn"
458
522
  }]
459
523
  ];
460
- const ListChecks = createLucideIcon("list-checks", __iconNode$19);
524
+ const ListChecks = createLucideIcon("list-checks", __iconNode$20);
461
525
 
462
526
  //#endregion
463
527
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/loader-circle.mjs
@@ -467,11 +531,11 @@ const ListChecks = createLucideIcon("list-checks", __iconNode$19);
467
531
  * This source code is licensed under the ISC license.
468
532
  * See the LICENSE file in the root directory of this source tree.
469
533
  */
470
- const __iconNode$18 = [["path", {
534
+ const __iconNode$19 = [["path", {
471
535
  d: "M21 12a9 9 0 1 1-6.219-8.56",
472
536
  key: "13zald"
473
537
  }]];
474
- const LoaderCircle = createLucideIcon("loader-circle", __iconNode$18);
538
+ const LoaderCircle = createLucideIcon("loader-circle", __iconNode$19);
475
539
 
476
540
  //#endregion
477
541
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/menu.mjs
@@ -481,7 +545,7 @@ const LoaderCircle = createLucideIcon("loader-circle", __iconNode$18);
481
545
  * This source code is licensed under the ISC license.
482
546
  * See the LICENSE file in the root directory of this source tree.
483
547
  */
484
- const __iconNode$17 = [
548
+ const __iconNode$18 = [
485
549
  ["path", {
486
550
  d: "M4 5h16",
487
551
  key: "1tepv9"
@@ -495,7 +559,7 @@ const __iconNode$17 = [
495
559
  key: "1djgab"
496
560
  }]
497
561
  ];
498
- const Menu = createLucideIcon("menu", __iconNode$17);
562
+ const Menu = createLucideIcon("menu", __iconNode$18);
499
563
 
500
564
  //#endregion
501
565
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/message-circle-question-mark.mjs
@@ -505,7 +569,7 @@ const Menu = createLucideIcon("menu", __iconNode$17);
505
569
  * This source code is licensed under the ISC license.
506
570
  * See the LICENSE file in the root directory of this source tree.
507
571
  */
508
- const __iconNode$16 = [
572
+ const __iconNode$17 = [
509
573
  ["path", {
510
574
  d: "M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719",
511
575
  key: "1sd12s"
@@ -519,7 +583,7 @@ const __iconNode$16 = [
519
583
  key: "p32p05"
520
584
  }]
521
585
  ];
522
- const MessageCircleQuestionMark = createLucideIcon("message-circle-question-mark", __iconNode$16);
586
+ const MessageCircleQuestionMark = createLucideIcon("message-circle-question-mark", __iconNode$17);
523
587
 
524
588
  //#endregion
525
589
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/panel-right-open.mjs
@@ -529,7 +593,7 @@ const MessageCircleQuestionMark = createLucideIcon("message-circle-question-mark
529
593
  * This source code is licensed under the ISC license.
530
594
  * See the LICENSE file in the root directory of this source tree.
531
595
  */
532
- const __iconNode$15 = [
596
+ const __iconNode$16 = [
533
597
  ["rect", {
534
598
  width: "18",
535
599
  height: "18",
@@ -547,7 +611,7 @@ const __iconNode$15 = [
547
611
  key: "1pgupc"
548
612
  }]
549
613
  ];
550
- const PanelRightOpen = createLucideIcon("panel-right-open", __iconNode$15);
614
+ const PanelRightOpen = createLucideIcon("panel-right-open", __iconNode$16);
551
615
 
552
616
  //#endregion
553
617
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/paperclip.mjs
@@ -557,11 +621,28 @@ const PanelRightOpen = createLucideIcon("panel-right-open", __iconNode$15);
557
621
  * This source code is licensed under the ISC license.
558
622
  * See the LICENSE file in the root directory of this source tree.
559
623
  */
560
- const __iconNode$14 = [["path", {
624
+ const __iconNode$15 = [["path", {
561
625
  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",
562
626
  key: "1miecu"
563
627
  }]];
564
- const Paperclip = createLucideIcon("paperclip", __iconNode$14);
628
+ const Paperclip = createLucideIcon("paperclip", __iconNode$15);
629
+
630
+ //#endregion
631
+ //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/pencil.mjs
632
+ /**
633
+ * @license lucide-react v1.27.0 - ISC
634
+ *
635
+ * This source code is licensed under the ISC license.
636
+ * See the LICENSE file in the root directory of this source tree.
637
+ */
638
+ const __iconNode$14 = [["path", {
639
+ d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
640
+ key: "1a8usu"
641
+ }], ["path", {
642
+ d: "m15 5 4 4",
643
+ key: "1mk7zo"
644
+ }]];
645
+ const Pencil = createLucideIcon("pencil", __iconNode$14);
565
646
 
566
647
  //#endregion
567
648
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/play.mjs
@@ -913,8 +994,8 @@ function Label({ className = "", ...props }) {
913
994
 
914
995
  //#endregion
915
996
  //#region ../../node_modules/.pnpm/@radix-ui+react-compose-refs@1.1.5_@types+react@19.2.17_react@19.2.8/node_modules/@radix-ui/react-compose-refs/dist/index.mjs
916
- var __defProp$22 = Object.defineProperty;
917
- var __name$22 = (target, value) => __defProp$22(target, "name", {
997
+ var __defProp$21 = Object.defineProperty;
998
+ var __name$21 = (target, value) => __defProp$21(target, "name", {
918
999
  value,
919
1000
  configurable: true
920
1001
  });
@@ -922,7 +1003,7 @@ function setRef$2(ref, value) {
922
1003
  if (typeof ref === "function") return ref(value);
923
1004
  else if (ref !== null && ref !== void 0) ref.current = value;
924
1005
  }
925
- __name$22(setRef$2, "setRef");
1006
+ __name$21(setRef$2, "setRef");
926
1007
  function composeRefs$1(...refs) {
927
1008
  return (node) => {
928
1009
  let hasCleanup = false;
@@ -940,16 +1021,16 @@ function composeRefs$1(...refs) {
940
1021
  };
941
1022
  };
942
1023
  }
943
- __name$22(composeRefs$1, "composeRefs");
1024
+ __name$21(composeRefs$1, "composeRefs");
944
1025
  function useComposedRefs$1(...refs) {
945
1026
  return React$1.useCallback(composeRefs$1(...refs), refs);
946
1027
  }
947
- __name$22(useComposedRefs$1, "useComposedRefs");
1028
+ __name$21(useComposedRefs$1, "useComposedRefs");
948
1029
 
949
1030
  //#endregion
950
1031
  //#region ../../node_modules/.pnpm/@radix-ui+react-context@1.2.2_@types+react@19.2.17_react@19.2.8/node_modules/@radix-ui/react-context/dist/index.mjs
951
- var __defProp$21 = Object.defineProperty;
952
- var __name$21 = (target, value) => __defProp$21(target, "name", {
1032
+ var __defProp$20 = Object.defineProperty;
1033
+ var __name$20 = (target, value) => __defProp$20(target, "name", {
953
1034
  value,
954
1035
  configurable: true
955
1036
  });
@@ -957,7 +1038,7 @@ var __name$21 = (target, value) => __defProp$21(target, "name", {
957
1038
  function createContext2(rootComponentName, defaultContext) {
958
1039
  const Context = React$1.createContext(defaultContext);
959
1040
  Context.displayName = rootComponentName + "Context";
960
- const Provider = /* @__PURE__ */ __name$21((props) => {
1041
+ const Provider = /* @__PURE__ */ __name$20((props) => {
961
1042
  const { children, ...context } = props;
962
1043
  const value = React$1.useMemo(() => context, Object.values(context));
963
1044
  return /* @__PURE__ */ jsx(Context.Provider, {
@@ -974,10 +1055,10 @@ function createContext2(rootComponentName, defaultContext) {
974
1055
  if (optional) return void 0;
975
1056
  throw new Error(`\`${consumerName}\` must be used within \`${rootComponentName}\``);
976
1057
  }
977
- __name$21(useContext2, "useContext");
1058
+ __name$20(useContext2, "useContext");
978
1059
  return [Provider, useContext2];
979
1060
  }
980
- __name$21(createContext2, "createContext");
1061
+ __name$20(createContext2, "createContext");
981
1062
  // @__NO_SIDE_EFFECTS__
982
1063
  function createContextScope(scopeName, createContextScopeDeps = []) {
983
1064
  let defaultContexts = [];
@@ -986,7 +1067,7 @@ function createContextScope(scopeName, createContextScopeDeps = []) {
986
1067
  BaseContext.displayName = rootComponentName + "Context";
987
1068
  const index = defaultContexts.length;
988
1069
  defaultContexts = [...defaultContexts, defaultContext];
989
- const Provider = /* @__PURE__ */ __name$21((props) => {
1070
+ const Provider = /* @__PURE__ */ __name$20((props) => {
990
1071
  const { scope, children, ...context } = props;
991
1072
  const Context = scope?.[scopeName]?.[index] || BaseContext;
992
1073
  const value = React$1.useMemo(() => context, Object.values(context));
@@ -1005,15 +1086,15 @@ function createContextScope(scopeName, createContextScopeDeps = []) {
1005
1086
  if (optional) return void 0;
1006
1087
  throw new Error(`\`${consumerName}\` must be used within \`${rootComponentName}\``);
1007
1088
  }
1008
- __name$21(useContext2, "useContext");
1089
+ __name$20(useContext2, "useContext");
1009
1090
  return [Provider, useContext2];
1010
1091
  }
1011
- __name$21(createContext3, "createContext");
1012
- const createScope = /* @__PURE__ */ __name$21(() => {
1092
+ __name$20(createContext3, "createContext");
1093
+ const createScope = /* @__PURE__ */ __name$20(() => {
1013
1094
  const scopeContexts = defaultContexts.map((defaultContext) => {
1014
1095
  return React$1.createContext(defaultContext);
1015
1096
  });
1016
- return /* @__PURE__ */ __name$21(function useScope(scope) {
1097
+ return /* @__PURE__ */ __name$20(function useScope(scope) {
1017
1098
  const contexts = scope?.[scopeName] || scopeContexts;
1018
1099
  return React$1.useMemo(() => ({ [`__scope${scopeName}`]: {
1019
1100
  ...scope,
@@ -1024,16 +1105,16 @@ function createContextScope(scopeName, createContextScopeDeps = []) {
1024
1105
  createScope.scopeName = scopeName;
1025
1106
  return [createContext3, composeContextScopes(createScope, ...createContextScopeDeps)];
1026
1107
  }
1027
- __name$21(createContextScope, "createContextScope");
1108
+ __name$20(createContextScope, "createContextScope");
1028
1109
  function composeContextScopes(...scopes) {
1029
1110
  const baseScope = scopes[0];
1030
1111
  if (scopes.length === 1) return baseScope;
1031
- const createScope = /* @__PURE__ */ __name$21(() => {
1112
+ const createScope = /* @__PURE__ */ __name$20(() => {
1032
1113
  const scopeHooks = scopes.map((createScope2) => ({
1033
1114
  useScope: createScope2(),
1034
1115
  scopeName: createScope2.scopeName
1035
1116
  }));
1036
- return /* @__PURE__ */ __name$21(function useComposedScopes(overrideScopes) {
1117
+ return /* @__PURE__ */ __name$20(function useComposedScopes(overrideScopes) {
1037
1118
  const nextScopes = scopeHooks.reduce((nextScopes2, { useScope, scopeName }) => {
1038
1119
  const currentScope = useScope(overrideScopes)[`__scope${scopeName}`];
1039
1120
  return {
@@ -1047,33 +1128,33 @@ function composeContextScopes(...scopes) {
1047
1128
  createScope.scopeName = baseScope.scopeName;
1048
1129
  return createScope;
1049
1130
  }
1050
- __name$21(composeContextScopes, "composeContextScopes");
1131
+ __name$20(composeContextScopes, "composeContextScopes");
1051
1132
 
1052
1133
  //#endregion
1053
1134
  //#region ../../node_modules/.pnpm/@radix-ui+primitive@1.1.7/node_modules/@radix-ui/primitive/dist/index.mjs
1054
- var __defProp$20 = Object.defineProperty;
1055
- var __name$20 = (target, value) => __defProp$20(target, "name", {
1135
+ var __defProp$19 = Object.defineProperty;
1136
+ var __name$19 = (target, value) => __defProp$19(target, "name", {
1056
1137
  value,
1057
1138
  configurable: true
1058
1139
  });
1059
1140
  var canUseDOM = !!(typeof window !== "undefined" && window.document && window.document.createElement);
1060
1141
  function composeEventHandlers(originalEventHandler, ourEventHandler, { checkForDefaultPrevented = true } = {}) {
1061
- return /* @__PURE__ */ __name$20(function handleEvent(event) {
1142
+ return /* @__PURE__ */ __name$19(function handleEvent(event) {
1062
1143
  originalEventHandler?.(event);
1063
1144
  if (checkForDefaultPrevented === false || !event || !event.defaultPrevented) return ourEventHandler?.(event);
1064
1145
  }, "handleEvent");
1065
1146
  }
1066
- __name$20(composeEventHandlers, "composeEventHandlers");
1147
+ __name$19(composeEventHandlers, "composeEventHandlers");
1067
1148
  function getOwnerWindow(element) {
1068
1149
  if (!canUseDOM) throw new Error("Cannot access window outside of the DOM");
1069
1150
  return element?.ownerDocument?.defaultView ?? window;
1070
1151
  }
1071
- __name$20(getOwnerWindow, "getOwnerWindow");
1152
+ __name$19(getOwnerWindow, "getOwnerWindow");
1072
1153
  function getOwnerDocument(element) {
1073
1154
  if (!canUseDOM) throw new Error("Cannot access document outside of the DOM");
1074
1155
  return element?.ownerDocument ?? document;
1075
1156
  }
1076
- __name$20(getOwnerDocument, "getOwnerDocument");
1157
+ __name$19(getOwnerDocument, "getOwnerDocument");
1077
1158
  function getActiveElement(node, activeDescendant = false) {
1078
1159
  const { activeElement } = getOwnerDocument(node);
1079
1160
  if (!activeElement?.nodeName) return null;
@@ -1087,11 +1168,11 @@ function getActiveElement(node, activeDescendant = false) {
1087
1168
  }
1088
1169
  return activeElement;
1089
1170
  }
1090
- __name$20(getActiveElement, "getActiveElement");
1171
+ __name$19(getActiveElement, "getActiveElement");
1091
1172
  function isFrame(element) {
1092
1173
  return element.tagName === "IFRAME";
1093
1174
  }
1094
- __name$20(isFrame, "isFrame");
1175
+ __name$19(isFrame, "isFrame");
1095
1176
 
1096
1177
  //#endregion
1097
1178
  //#region ../../node_modules/.pnpm/@radix-ui+react-use-layout-effect@1.1.4_@types+react@19.2.17_react@19.2.8/node_modules/@radix-ui/react-use-layout-effect/dist/index.mjs
@@ -1099,8 +1180,8 @@ var useLayoutEffect2 = globalThis?.document ? React$1.useLayoutEffect : () => {}
1099
1180
 
1100
1181
  //#endregion
1101
1182
  //#region ../../node_modules/.pnpm/@radix-ui+react-use-effect-event@0.0.5_@types+react@19.2.17_react@19.2.8/node_modules/@radix-ui/react-use-effect-event/dist/index.mjs
1102
- var __defProp$19 = Object.defineProperty;
1103
- var __name$19 = (target, value) => __defProp$19(target, "name", {
1183
+ var __defProp$18 = Object.defineProperty;
1184
+ var __name$18 = (target, value) => __defProp$18(target, "name", {
1104
1185
  value,
1105
1186
  configurable: true
1106
1187
  });
@@ -1119,17 +1200,17 @@ function useEffectEvent$1(callback) {
1119
1200
  });
1120
1201
  return React$1.useMemo(() => ((...args) => ref.current?.(...args)), []);
1121
1202
  }
1122
- __name$19(useEffectEvent$1, "useEffectEvent");
1203
+ __name$18(useEffectEvent$1, "useEffectEvent");
1123
1204
 
1124
1205
  //#endregion
1125
1206
  //#region ../../node_modules/.pnpm/@radix-ui+react-use-controllable-state@1.2.6_@types+react@19.2.17_react@19.2.8/node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
1126
- var __defProp$18 = Object.defineProperty;
1127
- var __name$18 = (target, value) => __defProp$18(target, "name", {
1207
+ var __defProp$17 = Object.defineProperty;
1208
+ var __name$17 = (target, value) => __defProp$17(target, "name", {
1128
1209
  value,
1129
1210
  configurable: true
1130
1211
  });
1131
1212
  var useInsertionEffect$1 = React$1[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
1132
- function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name$18(() => {}, "onChange"), caller }) {
1213
+ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name$17(() => {}, "onChange"), caller }) {
1133
1214
  const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
1134
1215
  defaultProp,
1135
1216
  onChange
@@ -1146,7 +1227,7 @@ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __
1146
1227
  }
1147
1228
  return [value, React$1.useCallback((nextValue) => {
1148
1229
  if (isControlled) {
1149
- const value2 = isFunction$2(nextValue) ? nextValue(prop) : nextValue;
1230
+ const value2 = isFunction$1(nextValue) ? nextValue(prop) : nextValue;
1150
1231
  if (value2 !== prop) onChangeRef.current?.(value2);
1151
1232
  } else setUncontrolledProp(nextValue);
1152
1233
  }, [
@@ -1156,7 +1237,7 @@ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __
1156
1237
  onChangeRef
1157
1238
  ])];
1158
1239
  }
1159
- __name$18(useControllableState, "useControllableState");
1240
+ __name$17(useControllableState, "useControllableState");
1160
1241
  function useUncontrolledState({ defaultProp, onChange }) {
1161
1242
  const [value, setValue] = React$1.useState(defaultProp);
1162
1243
  const prevValueRef = React$1.useRef(value);
@@ -1176,11 +1257,11 @@ function useUncontrolledState({ defaultProp, onChange }) {
1176
1257
  onChangeRef
1177
1258
  ];
1178
1259
  }
1179
- __name$18(useUncontrolledState, "useUncontrolledState");
1180
- function isFunction$2(value) {
1260
+ __name$17(useUncontrolledState, "useUncontrolledState");
1261
+ function isFunction$1(value) {
1181
1262
  return typeof value === "function";
1182
1263
  }
1183
- __name$18(isFunction$2, "isFunction");
1264
+ __name$17(isFunction$1, "isFunction");
1184
1265
  var SYNC_STATE = Symbol("RADIX:SYNC_STATE");
1185
1266
  function useControllableStateReducer(reducer, userArgs, initialArg, init) {
1186
1267
  const { prop: controlledState, defaultProp, onChange: onChangeProp, caller } = userArgs;
@@ -1239,12 +1320,12 @@ function useControllableStateReducer(reducer, userArgs, initialArg, init) {
1239
1320
  ]);
1240
1321
  return [state, dispatch];
1241
1322
  }
1242
- __name$18(useControllableStateReducer, "useControllableStateReducer");
1323
+ __name$17(useControllableStateReducer, "useControllableStateReducer");
1243
1324
 
1244
1325
  //#endregion
1245
1326
  //#region ../../node_modules/.pnpm/@radix-ui+react-use-size@1.1.4_@types+react@19.2.17_react@19.2.8/node_modules/@radix-ui/react-use-size/dist/index.mjs
1246
- var __defProp$17 = Object.defineProperty;
1247
- var __name$17 = (target, value) => __defProp$17(target, "name", {
1327
+ var __defProp$16 = Object.defineProperty;
1328
+ var __name$16 = (target, value) => __defProp$16(target, "name", {
1248
1329
  value,
1249
1330
  configurable: true
1250
1331
  });
@@ -1282,12 +1363,12 @@ function useSize(element) {
1282
1363
  }, [element]);
1283
1364
  return size;
1284
1365
  }
1285
- __name$17(useSize, "useSize");
1366
+ __name$16(useSize, "useSize");
1286
1367
 
1287
1368
  //#endregion
1288
1369
  //#region ../../node_modules/.pnpm/@radix-ui+react-presence@1.1.10_@types+react-dom@19.2.3_@types+react@19.2.17__@types+re_138587ba593102bdcf335a5673d92068/node_modules/@radix-ui/react-presence/dist/index.mjs
1289
- var __defProp$16 = Object.defineProperty;
1290
- var __name$16 = (target, value) => __defProp$16(target, "name", {
1370
+ var __defProp$15 = Object.defineProperty;
1371
+ var __name$15 = (target, value) => __defProp$15(target, "name", {
1291
1372
  value,
1292
1373
  configurable: true
1293
1374
  });
@@ -1296,8 +1377,8 @@ function useStateMachine(initialState, machine) {
1296
1377
  return machine[state][event] ?? state;
1297
1378
  }, initialState);
1298
1379
  }
1299
- __name$16(useStateMachine, "useStateMachine");
1300
- var Presence = /* @__PURE__ */ __name$16((props) => {
1380
+ __name$15(useStateMachine, "useStateMachine");
1381
+ var Presence = /* @__PURE__ */ __name$15((props) => {
1301
1382
  const { present, children } = props;
1302
1383
  const presence = usePresence$1(present);
1303
1384
  const child = typeof children === "function" ? children({ present: presence.isPresent }) : React$1.Children.only(children);
@@ -1346,7 +1427,7 @@ function usePresence$1(present) {
1346
1427
  if (node) {
1347
1428
  let timeoutId;
1348
1429
  const ownerWindow = node.ownerDocument.defaultView ?? window;
1349
- const handleAnimationEnd = /* @__PURE__ */ __name$16((event) => {
1430
+ const handleAnimationEnd = /* @__PURE__ */ __name$15((event) => {
1350
1431
  const isCurrentAnimation = getAnimationName(stylesRef.current).includes(CSS.escape(event.animationName));
1351
1432
  if (event.target === node && isCurrentAnimation) {
1352
1433
  send("ANIMATION_END");
@@ -1359,7 +1440,7 @@ function usePresence$1(present) {
1359
1440
  }
1360
1441
  }
1361
1442
  }, "handleAnimationEnd");
1362
- const handleAnimationStart = /* @__PURE__ */ __name$16((event) => {
1443
+ const handleAnimationStart = /* @__PURE__ */ __name$15((event) => {
1363
1444
  if (event.target === node) prevAnimationNameRef.current = getAnimationName(stylesRef.current);
1364
1445
  }, "handleAnimationStart");
1365
1446
  node.addEventListener("animationstart", handleAnimationStart);
@@ -1385,12 +1466,12 @@ function usePresence$1(present) {
1385
1466
  }, [])
1386
1467
  };
1387
1468
  }
1388
- __name$16(usePresence$1, "usePresence");
1469
+ __name$15(usePresence$1, "usePresence");
1389
1470
  function setRef$1(ref, value) {
1390
1471
  if (typeof ref === "function") return ref(value);
1391
1472
  else if (ref !== null && ref !== void 0) ref.current = value;
1392
1473
  }
1393
- __name$16(setRef$1, "setRef");
1474
+ __name$15(setRef$1, "setRef");
1394
1475
  function useStableComposedRefs(...refs) {
1395
1476
  const refsRef = React$1.useRef(refs);
1396
1477
  refsRef.current = refs;
@@ -1411,11 +1492,11 @@ function useStableComposedRefs(...refs) {
1411
1492
  };
1412
1493
  }, []);
1413
1494
  }
1414
- __name$16(useStableComposedRefs, "useStableComposedRefs");
1495
+ __name$15(useStableComposedRefs, "useStableComposedRefs");
1415
1496
  function getAnimationName(styles) {
1416
1497
  return styles?.animationName || "none";
1417
1498
  }
1418
- __name$16(getAnimationName, "getAnimationName");
1499
+ __name$15(getAnimationName, "getAnimationName");
1419
1500
  function getElementRef$1(element) {
1420
1501
  let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
1421
1502
  let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
@@ -1425,12 +1506,12 @@ function getElementRef$1(element) {
1425
1506
  if (mayWarn) return element.props.ref;
1426
1507
  return element.props.ref || element.ref;
1427
1508
  }
1428
- __name$16(getElementRef$1, "getElementRef");
1509
+ __name$15(getElementRef$1, "getElementRef");
1429
1510
 
1430
1511
  //#endregion
1431
1512
  //#region ../../node_modules/.pnpm/@radix-ui+react-slot@1.3.3_@types+react@19.2.17_react@19.2.8/node_modules/@radix-ui/react-slot/dist/index.mjs
1432
- var __defProp$15 = Object.defineProperty;
1433
- var __name$15 = (target, value) => __defProp$15(target, "name", {
1513
+ var __defProp$14 = Object.defineProperty;
1514
+ var __name$14 = (target, value) => __defProp$14(target, "name", {
1434
1515
  value,
1435
1516
  configurable: true
1436
1517
  });
@@ -1467,17 +1548,17 @@ function createSlot(ownerName) {
1467
1548
  Slot2.displayName = `${ownerName}.Slot`;
1468
1549
  return Slot2;
1469
1550
  }
1470
- __name$15(createSlot, "createSlot");
1551
+ __name$14(createSlot, "createSlot");
1471
1552
  var SLOTTABLE_IDENTIFIER = Symbol.for("radix.slottable");
1472
1553
  // @__NO_SIDE_EFFECTS__
1473
1554
  function createSlottable(ownerName) {
1474
- const Slottable2 = /* @__PURE__ */ __name$15((props) => "child" in props ? props.children(props.child) : props.children, "Slottable");
1555
+ const Slottable2 = /* @__PURE__ */ __name$14((props) => "child" in props ? props.children(props.child) : props.children, "Slottable");
1475
1556
  Slottable2.displayName = `${ownerName}.Slottable`;
1476
1557
  Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
1477
1558
  return Slottable2;
1478
1559
  }
1479
- __name$15(createSlottable, "createSlottable");
1480
- var getSlottableElementFromSlottable = /* @__PURE__ */ __name$15((slottable, child) => {
1560
+ __name$14(createSlottable, "createSlottable");
1561
+ var getSlottableElementFromSlottable = /* @__PURE__ */ __name$14((slottable, child) => {
1481
1562
  if ("child" in slottable.props) {
1482
1563
  const child2 = slottable.props.child;
1483
1564
  if (!React$1.isValidElement(child2)) return null;
@@ -1508,7 +1589,7 @@ function mergeProps(slotProps, childProps) {
1508
1589
  ...overrideProps
1509
1590
  };
1510
1591
  }
1511
- __name$15(mergeProps, "mergeProps");
1592
+ __name$14(mergeProps, "mergeProps");
1512
1593
  function getElementRef(element) {
1513
1594
  let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
1514
1595
  let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
@@ -1518,32 +1599,32 @@ function getElementRef(element) {
1518
1599
  if (mayWarn) return element.props.ref;
1519
1600
  return element.props.ref || element.ref;
1520
1601
  }
1521
- __name$15(getElementRef, "getElementRef");
1602
+ __name$14(getElementRef, "getElementRef");
1522
1603
  function isSlottable(child) {
1523
1604
  return React$1.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
1524
1605
  }
1525
- __name$15(isSlottable, "isSlottable");
1606
+ __name$14(isSlottable, "isSlottable");
1526
1607
  var REACT_LAZY_TYPE = Symbol.for("react.lazy");
1527
1608
  function isLazyComponent(element) {
1528
1609
  return element != null && typeof element === "object" && "$$typeof" in element && element.$$typeof === REACT_LAZY_TYPE && "_payload" in element && isPromiseLike(element._payload);
1529
1610
  }
1530
- __name$15(isLazyComponent, "isLazyComponent");
1611
+ __name$14(isLazyComponent, "isLazyComponent");
1531
1612
  function isPromiseLike(value) {
1532
1613
  return typeof value === "object" && value !== null && "then" in value;
1533
1614
  }
1534
- __name$15(isPromiseLike, "isPromiseLike");
1535
- var createSlotError = /* @__PURE__ */ __name$15((ownerName) => {
1615
+ __name$14(isPromiseLike, "isPromiseLike");
1616
+ var createSlotError = /* @__PURE__ */ __name$14((ownerName) => {
1536
1617
  return `${ownerName} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`;
1537
1618
  }, "createSlotError");
1538
- var createSlottableError = /* @__PURE__ */ __name$15((ownerName) => {
1619
+ var createSlottableError = /* @__PURE__ */ __name$14((ownerName) => {
1539
1620
  return `${ownerName} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`;
1540
1621
  }, "createSlottableError");
1541
1622
  var use = React$1[" use ".trim().toString()];
1542
1623
 
1543
1624
  //#endregion
1544
1625
  //#region ../../node_modules/.pnpm/@radix-ui+react-primitive@2.1.10_@types+react-dom@19.2.3_@types+react@19.2.17__@types+r_be17283a23f513d7b972453cc36e2a96/node_modules/@radix-ui/react-primitive/dist/index.mjs
1545
- var __defProp$14 = Object.defineProperty;
1546
- var __name$14 = (target, value) => __defProp$14(target, "name", {
1626
+ var __defProp$13 = Object.defineProperty;
1627
+ var __name$13 = (target, value) => __defProp$13(target, "name", {
1547
1628
  value,
1548
1629
  configurable: true
1549
1630
  });
@@ -1585,230 +1666,7 @@ var Primitive = [
1585
1666
  function dispatchDiscreteCustomEvent(target, event) {
1586
1667
  if (target) ReactDOM.flushSync(() => target.dispatchEvent(event));
1587
1668
  }
1588
- __name$14(dispatchDiscreteCustomEvent, "dispatchDiscreteCustomEvent");
1589
-
1590
- //#endregion
1591
- //#region ../../node_modules/.pnpm/@radix-ui+react-checkbox@1.3.11_@types+react-dom@19.2.3_@types+react@19.2.17__@types+re_f6e0f4835f2e28dbd2e5cb3844d005eb/node_modules/@radix-ui/react-checkbox/dist/index.mjs
1592
- var __defProp$13 = Object.defineProperty;
1593
- var __name$13 = (target, value) => __defProp$13(target, "name", {
1594
- value,
1595
- configurable: true
1596
- });
1597
- var CHECKBOX_NAME = "Checkbox";
1598
- var [createCheckboxContext, createCheckboxScope] = createContextScope(CHECKBOX_NAME);
1599
- var [CheckboxProviderImpl, useCheckboxContext] = createCheckboxContext(CHECKBOX_NAME);
1600
- function CheckboxProvider(props) {
1601
- const { __scopeCheckbox, checked: checkedProp, children, defaultChecked, disabled, form, name, onCheckedChange, required, value = "on", internal_do_not_use_render } = props;
1602
- const [checked, setChecked] = useControllableState({
1603
- prop: checkedProp,
1604
- defaultProp: defaultChecked ?? false,
1605
- onChange: onCheckedChange,
1606
- caller: CHECKBOX_NAME
1607
- });
1608
- const [control, setControl] = React$1.useState(null);
1609
- const [bubbleInput, setBubbleInput] = React$1.useState(null);
1610
- const hasConsumerStoppedPropagationRef = React$1.useRef(false);
1611
- const [userInteractionCount, onUserInteraction] = React$1.useReducer((count) => count + 1, 0);
1612
- const isFormControl = control ? !!form || !!control.closest("form") : true;
1613
- const context = {
1614
- checked,
1615
- disabled,
1616
- setChecked,
1617
- control,
1618
- setControl,
1619
- name,
1620
- form,
1621
- value,
1622
- hasConsumerStoppedPropagationRef,
1623
- userInteractionCount,
1624
- onUserInteraction,
1625
- required,
1626
- defaultChecked: isIndeterminate(defaultChecked) ? false : defaultChecked,
1627
- isFormControl,
1628
- bubbleInput,
1629
- setBubbleInput
1630
- };
1631
- return /* @__PURE__ */ jsx(CheckboxProviderImpl, {
1632
- scope: __scopeCheckbox,
1633
- ...context,
1634
- children: isFunction$1(internal_do_not_use_render) ? internal_do_not_use_render(context) : children
1635
- });
1636
- }
1637
- __name$13(CheckboxProvider, "CheckboxProvider");
1638
- var TRIGGER_NAME$3 = "CheckboxTrigger";
1639
- var CheckboxTrigger = /* @__PURE__ */ React$1.forwardRef(/* @__PURE__ */ __name$13(function CheckboxTrigger2({ __scopeCheckbox, onKeyDown, onClick, ...checkboxProps }, forwardedRef) {
1640
- const { control, value, disabled, checked, required, setControl, setChecked, hasConsumerStoppedPropagationRef, onUserInteraction, isFormControl, bubbleInput } = useCheckboxContext(TRIGGER_NAME$3, __scopeCheckbox);
1641
- const composedRefs = useComposedRefs$1(forwardedRef, setControl);
1642
- const initialCheckedStateRef = React$1.useRef(checked);
1643
- React$1.useEffect(() => {
1644
- const form = control?.form;
1645
- if (form) {
1646
- const reset = /* @__PURE__ */ __name$13(() => setChecked(initialCheckedStateRef.current), "reset");
1647
- form.addEventListener("reset", reset);
1648
- return () => form.removeEventListener("reset", reset);
1649
- }
1650
- }, [control, setChecked]);
1651
- return /* @__PURE__ */ jsx(Primitive.button, {
1652
- type: "button",
1653
- role: "checkbox",
1654
- "aria-checked": isIndeterminate(checked) ? "mixed" : checked,
1655
- "aria-required": required,
1656
- "data-state": getState$3(checked),
1657
- "data-disabled": disabled ? "" : void 0,
1658
- disabled,
1659
- value,
1660
- ...checkboxProps,
1661
- ref: composedRefs,
1662
- onKeyDown: composeEventHandlers(onKeyDown, (event) => {
1663
- if (event.key === "Enter") event.preventDefault();
1664
- }),
1665
- onClick: composeEventHandlers(onClick, (event) => {
1666
- onUserInteraction();
1667
- setChecked((prevChecked) => isIndeterminate(prevChecked) ? true : !prevChecked);
1668
- if (bubbleInput && isFormControl) {
1669
- hasConsumerStoppedPropagationRef.current = event.isPropagationStopped();
1670
- if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation();
1671
- }
1672
- })
1673
- });
1674
- }, "CheckboxTrigger"));
1675
- var Checkbox$1 = /* @__PURE__ */ React$1.forwardRef(/* @__PURE__ */ __name$13(function Checkbox2(props, forwardedRef) {
1676
- const { __scopeCheckbox, name, checked, defaultChecked, required, disabled, value, onCheckedChange, form, ...checkboxProps } = props;
1677
- return /* @__PURE__ */ jsx(CheckboxProvider, {
1678
- __scopeCheckbox,
1679
- checked,
1680
- defaultChecked,
1681
- disabled,
1682
- required,
1683
- onCheckedChange,
1684
- name,
1685
- form,
1686
- value,
1687
- internal_do_not_use_render: ({ isFormControl }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CheckboxTrigger, {
1688
- ...checkboxProps,
1689
- ref: forwardedRef,
1690
- __scopeCheckbox
1691
- }), isFormControl && /* @__PURE__ */ jsx(CheckboxBubbleInput, { __scopeCheckbox })] })
1692
- });
1693
- }, "Checkbox"));
1694
- var INDICATOR_NAME = "CheckboxIndicator";
1695
- var CheckboxIndicator = /* @__PURE__ */ React$1.forwardRef(/* @__PURE__ */ __name$13(function CheckboxIndicator2(props, forwardedRef) {
1696
- const { __scopeCheckbox, forceMount, ...indicatorProps } = props;
1697
- const context = useCheckboxContext(INDICATOR_NAME, __scopeCheckbox);
1698
- return /* @__PURE__ */ jsx(Presence, {
1699
- present: forceMount || isIndeterminate(context.checked) || context.checked === true,
1700
- children: /* @__PURE__ */ jsx(Primitive.span, {
1701
- "data-state": getState$3(context.checked),
1702
- "data-disabled": context.disabled ? "" : void 0,
1703
- ...indicatorProps,
1704
- ref: forwardedRef,
1705
- style: {
1706
- pointerEvents: "none",
1707
- ...props.style
1708
- }
1709
- })
1710
- });
1711
- }, "CheckboxIndicator"));
1712
- var BUBBLE_INPUT_NAME$1 = "CheckboxBubbleInput";
1713
- var CheckboxBubbleInput = /* @__PURE__ */ React$1.forwardRef(/* @__PURE__ */ __name$13(function CheckboxBubbleInput2({ __scopeCheckbox, onClick, ...props }, forwardedRef) {
1714
- const { control, hasConsumerStoppedPropagationRef, userInteractionCount, checked, defaultChecked, required, disabled, name, value, form, bubbleInput, setBubbleInput } = useCheckboxContext(BUBBLE_INPUT_NAME$1, __scopeCheckbox);
1715
- const composedRefs = useComposedRefs$1(forwardedRef, setBubbleInput);
1716
- const controlSize = useSize(control);
1717
- const shouldStopClickPropagationRef = React$1.useRef(false);
1718
- const prevCheckedRef = React$1.useRef(checked);
1719
- const prevUserInteractionCountRef = React$1.useRef(userInteractionCount);
1720
- React$1.useEffect(() => {
1721
- const input = bubbleInput;
1722
- if (!input) return;
1723
- const inputProto = window.HTMLInputElement.prototype;
1724
- const setChecked = Object.getOwnPropertyDescriptor(inputProto, "checked").set;
1725
- const isUserInteraction = userInteractionCount !== prevUserInteractionCountRef.current;
1726
- prevUserInteractionCountRef.current = userInteractionCount;
1727
- const checkedChanged = prevCheckedRef.current !== checked;
1728
- prevCheckedRef.current = checked;
1729
- const bubbles = !(isUserInteraction && hasConsumerStoppedPropagationRef.current);
1730
- if (checkedChanged && setChecked) {
1731
- shouldStopClickPropagationRef.current = !isUserInteraction;
1732
- const event = new Event("click", { bubbles });
1733
- input.indeterminate = isIndeterminate(checked);
1734
- setChecked.call(input, isIndeterminate(checked) ? false : checked);
1735
- input.dispatchEvent(event);
1736
- shouldStopClickPropagationRef.current = false;
1737
- }
1738
- }, [
1739
- bubbleInput,
1740
- checked,
1741
- hasConsumerStoppedPropagationRef,
1742
- userInteractionCount
1743
- ]);
1744
- const defaultCheckedRef = React$1.useRef(isIndeterminate(checked) ? false : checked);
1745
- return /* @__PURE__ */ jsx(Primitive.input, {
1746
- type: "checkbox",
1747
- "aria-hidden": true,
1748
- defaultChecked: defaultChecked ?? defaultCheckedRef.current,
1749
- required,
1750
- disabled,
1751
- name,
1752
- value,
1753
- form,
1754
- ...props,
1755
- tabIndex: -1,
1756
- ref: composedRefs,
1757
- onClick: composeEventHandlers(onClick, (event) => {
1758
- if (shouldStopClickPropagationRef.current) event.stopPropagation();
1759
- }),
1760
- style: {
1761
- ...props.style,
1762
- ...controlSize,
1763
- position: "absolute",
1764
- pointerEvents: "none",
1765
- opacity: 0,
1766
- margin: 0,
1767
- transform: "translateX(-100%)"
1768
- }
1769
- });
1770
- }, "CheckboxBubbleInput"));
1771
- function isFunction$1(value) {
1772
- return typeof value === "function";
1773
- }
1774
- __name$13(isFunction$1, "isFunction");
1775
- function isIndeterminate(checked) {
1776
- return checked === "indeterminate";
1777
- }
1778
- __name$13(isIndeterminate, "isIndeterminate");
1779
- function getState$3(checked) {
1780
- return isIndeterminate(checked) ? "indeterminate" : checked ? "checked" : "unchecked";
1781
- }
1782
- __name$13(getState$3, "getState");
1783
-
1784
- //#endregion
1785
- //#region ../ui-kit/src/components/Checkbox.tsx
1786
- /** Source-owned shadcn-style checkbox backed by Radix for keyboard semantics. */
1787
- function Checkbox({ className = "", invalid = false, "aria-invalid": ariaInvalid, ...props }) {
1788
- return /* @__PURE__ */ jsx(Checkbox$1, {
1789
- "aria-invalid": invalid ? true : ariaInvalid,
1790
- className: `inline-flex size-4 shrink-0 items-center justify-center rounded-sm border bg-surface shadow-xs outline-none transition-colors disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground ${invalid ? "border-destructive focus-visible:ring-destructive/20" : "border-input focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/20"} ${className}`,
1791
- ...props,
1792
- children: /* @__PURE__ */ jsx(CheckboxIndicator, {
1793
- className: "flex items-center justify-center",
1794
- children: /* @__PURE__ */ jsx(Check, {
1795
- className: "size-3",
1796
- "aria-hidden": true
1797
- })
1798
- })
1799
- });
1800
- }
1801
-
1802
- //#endregion
1803
- //#region ../ui-kit/src/components/Textarea.tsx
1804
- /** Source-owned textarea with the same form-control states as Input. */
1805
- function Textarea({ className = "", invalid = false, "aria-invalid": ariaInvalid, ...props }) {
1806
- return /* @__PURE__ */ jsx("textarea", {
1807
- "aria-invalid": invalid ? true : ariaInvalid,
1808
- className: `min-h-20 w-full resize-y rounded-md border bg-surface px-3 py-2 text-sm text-ink shadow-xs outline-none transition-colors placeholder:text-muted disabled:cursor-not-allowed disabled:opacity-50 ${invalid ? "border-destructive focus-visible:ring-destructive/20" : "border-input focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/20"} ${className}`,
1809
- ...props
1810
- });
1811
- }
1669
+ __name$13(dispatchDiscreteCustomEvent, "dispatchDiscreteCustomEvent");
1812
1670
 
1813
1671
  //#endregion
1814
1672
  //#region ../../node_modules/.pnpm/@radix-ui+react-switch@1.3.7_@types+react-dom@19.2.3_@types+react@19.2.17__@types+react_e96baf15833549650471eefa4ffd78d0/node_modules/@radix-ui/react-switch/dist/index.mjs
@@ -40690,6 +40548,10 @@ function defaultRuntimeConfig() {
40690
40548
  approvalScope: "once-per-run"
40691
40549
  },
40692
40550
  router: { strategy: "progressive" },
40551
+ hooks: {
40552
+ timeoutMs: 5e3,
40553
+ failOnHookError: false
40554
+ },
40693
40555
  streaming: true,
40694
40556
  renderResult: true,
40695
40557
  sandbox: {
@@ -40699,6 +40561,10 @@ function defaultRuntimeConfig() {
40699
40561
  readReference: true,
40700
40562
  writeArtifact: true,
40701
40563
  confirm: true
40564
+ },
40565
+ remoteUrl: {
40566
+ allowHttp: false,
40567
+ allowPrivateHosts: false
40702
40568
  }
40703
40569
  },
40704
40570
  llm: {
@@ -40719,6 +40585,7 @@ function mergeRuntimeConfigDefaults(partial) {
40719
40585
  loop: sub("loop", d.loop),
40720
40586
  interaction: sub("interaction", d.interaction),
40721
40587
  router: sub("router", d.router),
40588
+ hooks: sub("hooks", d.hooks),
40722
40589
  streaming: typeof p["streaming"] === "boolean" ? p["streaming"] : d.streaming,
40723
40590
  renderResult: typeof p["renderResult"] === "boolean" ? p["renderResult"] : d.renderResult,
40724
40591
  sandbox: {
@@ -40726,6 +40593,10 @@ function mergeRuntimeConfigDefaults(partial) {
40726
40593
  capabilities: {
40727
40594
  ...d.sandbox.capabilities,
40728
40595
  ...p["sandbox"]?.["capabilities"] ?? {}
40596
+ },
40597
+ remoteUrl: {
40598
+ ...d.sandbox.remoteUrl,
40599
+ ...p["sandbox"]?.["remoteUrl"] ?? {}
40729
40600
  }
40730
40601
  },
40731
40602
  llm: sub("llm", d.llm)
@@ -40747,8 +40618,36 @@ function withRuntimeConfigDefaults(store) {
40747
40618
  //#endregion
40748
40619
  //#region src/core/interactionBridge.ts
40749
40620
  /**
40621
+ * 渲染档 → 能力集。四档在 0.4.0 起 interaction/surface 全部等价,
40622
+ * 差别只剩「是否依赖 optional peer」。作为可注入值传入 CompositeUiBridge,
40623
+ * 便于宿主按实际装配情况覆盖(例如已确认某 peer 不可用)。
40624
+ * @experimental
40625
+ */
40626
+ const DEFAULT_RENDERER_CAPABILITIES = {
40627
+ native: {
40628
+ interaction: true,
40629
+ surface: true
40630
+ },
40631
+ a2ui: {
40632
+ interaction: true,
40633
+ surface: true,
40634
+ requiresPeer: "@a2ui/lit"
40635
+ },
40636
+ openui: {
40637
+ interaction: true,
40638
+ surface: true,
40639
+ requiresPeer: "@openuidev/react-lang"
40640
+ },
40641
+ vercel: {
40642
+ interaction: true,
40643
+ surface: true,
40644
+ requiresPeer: "@json-render/react"
40645
+ }
40646
+ };
40647
+ /**
40750
40648
  * 探测 A2UI Lit 渲染器是否可用(@a2ui/lit 为 optional peer,未安装时 import 失败)。
40751
40649
  * Appearance/设置区据此对 A2UI 档置灰;仅动态 import,不强依赖。
40650
+ * @experimental
40752
40651
  */
40753
40652
  async function probeA2uiAvailability() {
40754
40653
  try {
@@ -40763,6 +40662,7 @@ async function probeA2uiAvailability() {
40763
40662
  * 经 @lit/context 向 surface 子树注入 renderMarkdown(markdown-it + dompurify),
40764
40663
  * 消除 "[MarkdownDirective] can't render markdown" 降级。
40765
40664
  * 未安装时返回 false(宿主可据此保留产品化限制说明)。
40665
+ * @experimental
40766
40666
  */
40767
40667
  async function configureA2uiMarkdown(mount) {
40768
40668
  let deps;
@@ -40790,6 +40690,7 @@ async function configureA2uiMarkdown(mount) {
40790
40690
  /**
40791
40691
  * 探测 OpenUI 渲染器是否可用(@openuidev/react-lang 为 optional peer)。
40792
40692
  * Appearance/设置区据此对 OpenUI 档置灰;仅动态 import,不强依赖。
40693
+ * @experimental
40793
40694
  */
40794
40695
  async function probeOpenUiAvailability() {
40795
40696
  try {
@@ -40799,135 +40700,52 @@ async function probeOpenUiAvailability() {
40799
40700
  return false;
40800
40701
  }
40801
40702
  }
40802
- /**
40803
- * OpenUI 交互通道(core 层,不 import react):
40804
- * - request() 把 InteractionRequest 转成 openui-lang 文本并挂起,react 层
40805
- * (OpenUiInteraction)订阅 current 后动态加载 @openuidev/react-lang 渲染;
40806
- * - 动作事件经 handleAction → fromOpenUiAction(含 authorize Allow/Deny 语义)回传;
40807
- * - react 层初始化失败调 fail():挂起请求 reject(桥据此粘性降级 native)并记录原因;
40808
- * - attach/detach 标记渲染视图是否挂载(未挂载时桥直接降级 native,不悬挂请求)。
40809
- */
40810
- var OpenUiChannel = class {
40811
- #current;
40812
- #listeners = /* @__PURE__ */ new Set();
40813
- #attached = 0;
40814
- #failure;
40815
- /** React useSyncExternalStore 兼容订阅 */
40816
- subscribe = (listener) => {
40817
- this.#listeners.add(listener);
40818
- return () => this.#listeners.delete(listener);
40819
- };
40820
- get current() {
40821
- return this.#current?.session;
40822
- }
40823
- get isAttached() {
40824
- return this.#attached > 0;
40825
- }
40826
- /** react 层初始化失败原因(未安装 @openuidev/react-lang 等);记录后粘性生效 */
40827
- get failure() {
40828
- return this.#failure;
40829
- }
40830
- attach() {
40831
- this.#attached += 1;
40832
- }
40833
- detach() {
40834
- this.#attached = Math.max(0, this.#attached - 1);
40835
- }
40836
- request(input) {
40837
- if (this.#current) this.#settle({
40838
- id: this.#current.session.request.id,
40839
- cancelled: true
40840
- });
40841
- return new Promise((resolve, reject) => {
40842
- this.#current = {
40843
- session: {
40844
- request: input,
40845
- lang: toOpenUiLang(input)
40846
- },
40847
- resolve,
40848
- reject
40849
- };
40850
- this.#emit();
40851
- });
40852
- }
40853
- /** runtime 交互超时/取消:以 cancelled resolve 挂起的 request(防悬挂 + 残留) */
40854
- cancel(id) {
40855
- if (this.#current?.session.request.id === id) this.#settle({
40856
- id,
40857
- cancelled: true
40858
- });
40859
- }
40860
- /** react 层 OpenUI 动作回传(fromOpenUiAction:Allow → value:true;Deny/Cancel → cancelled:true) */
40861
- handleAction(action) {
40862
- if (!this.#current) return;
40863
- this.#settle(fromOpenUiAction(action));
40864
- }
40865
- /** react 层初始化失败:挂起请求 reject 并记录原因(桥读取后降级 native) */
40866
- fail(message) {
40867
- this.#failure = message;
40868
- const pending = this.#current;
40869
- this.#current = void 0;
40870
- this.#emit();
40871
- pending?.reject(new Error(message));
40872
- }
40873
- #settle(response) {
40874
- const pending = this.#current;
40875
- this.#current = void 0;
40876
- this.#emit();
40877
- pending?.resolve(response);
40878
- }
40879
- #emit() {
40880
- for (const listener of [...this.#listeners]) listener();
40881
- }
40703
+ /** 渲染档在告警文案里的显示名(面向用户,不用内部 kind 字面量) */
40704
+ const RENDERER_LABEL = {
40705
+ native: "native",
40706
+ a2ui: "A2UI",
40707
+ openui: "OpenUI",
40708
+ vercel: "json-render"
40882
40709
  };
40883
40710
  /**
40884
40711
  * 复合 UiBridge(P2/P3 渲染框架档):
40885
40712
  * - streamingText / renderResult 始终委托 ReactBridgeState(结果块数据源不变);
40886
40713
  * - 交互 request/cancel 按渲染档分发:native → ReactBridgeState(InteractionCard),
40887
- * a2uiLitRendererBridge(@a2ui/lit,容器由 Chatbot 提供),
40888
- * openui OpenUiChannel(@openuidev/react-lang Renderer react 层装配),
40889
- * vercel ReactBridgeState(payload 预览卡消费 bridge.pending,仅可取消);
40890
- * - 每次交互开始/结束发 interaction-requested/resolved 事件(UI 据此显隐 a2ui 容器);
40891
- * - a2ui/openui 初始化失败(optional peer 未安装等)降级 native + warning 事件,不阻断 run。
40714
+ * 其余三档SpecInteractionChannel(core 产出 catalog 节点树,react 层交给本档
40715
+ * 已有的 spec 渲染路径:A2uiSpecSurface / OpenUiSpecSurface / JsonRenderSpecSurface);
40716
+ * - 每次交互开始/结束发 interaction-requested/resolved 事件;
40717
+ * - native 档初始化失败(optional peer 未安装等)降级 native + warning 事件,不阻断 run。
40718
+ * @stable
40892
40719
  */
40893
40720
  var CompositeUiBridge = class {
40894
40721
  #deps;
40895
- #a2ui;
40896
- /** a2ui 失败/无容器后粘性降级 native(同一引擎内不逐请求重复告警) */
40722
+ #capabilities;
40723
+ /** native 档失败/未挂载后粘性降级 native(同一引擎内不逐请求重复告警) */
40897
40724
  #downgraded = false;
40898
40725
  constructor(deps) {
40899
40726
  this.#deps = deps;
40727
+ this.#capabilities = deps.capabilities ?? DEFAULT_RENDERER_CAPABILITIES;
40900
40728
  }
40901
- /** 渲染档热切换:丢弃已建的 a2ui 桥与降级状态(新档立即生效,无需重装配 runtime) */
40729
+ /** 渲染档热切换:清降级状态(新档立即生效,无需重装配 runtime) */
40902
40730
  reset() {
40903
- this.#a2ui = void 0;
40904
40731
  this.#downgraded = false;
40905
40732
  }
40906
40733
  async request(input) {
40907
40734
  const renderer = this.#deps.getRenderer();
40908
- if (renderer === "a2ui" && !this.#downgraded) {
40909
- const bridge = this.#a2uiBridge();
40910
- if (bridge === void 0) this.#downgrade("The A2UI renderer is selected but its interaction container is not attached; falling back to native interaction cards");
40911
- else return this.#requestA2ui(bridge, input);
40912
- }
40913
- if (renderer === "openui" && !this.#downgraded) {
40914
- const channel = this.#deps.getOpenUiChannel();
40735
+ if (this.#inTier(renderer)) {
40736
+ const channel = this.#deps.getInteractionChannel();
40737
+ const label = RENDERER_LABEL[renderer];
40915
40738
  const failure = channel.failure;
40916
- if (failure !== void 0) this.#downgrade(`The OpenUI renderer failed to initialize (${failure}); falling back to native interaction cards`);
40917
- else if (!channel.isAttached) this.#downgrade("The OpenUI renderer is selected but its interaction view is not attached; falling back to native interaction cards");
40918
- else return this.#requestOpenUi(channel, input);
40739
+ if (failure !== void 0) this.#downgrade(`The ${label} renderer failed to initialize (${failure}); falling back to native interaction cards`);
40740
+ else if (!channel.isAttached) this.#downgrade(`The ${label} renderer is selected but its interaction view is not attached; falling back to native interaction cards`);
40741
+ else return this.#requestInTier(channel, label, input);
40919
40742
  }
40920
40743
  return this.#requestNative(input);
40921
40744
  }
40922
- /** runtime 交互超时/取消:按档委托(a2ui/openui cancel 会以 cancelled resolve 挂起的 request) */
40745
+ /** runtime 交互超时/取消:按档委托(通道 cancel 会以 cancelled resolve 挂起的 request) */
40923
40746
  cancel(id) {
40924
- const renderer = this.#deps.getRenderer();
40925
- if (renderer === "a2ui" && this.#a2ui && !this.#downgraded) {
40926
- this.#a2ui.bridge.cancel(id);
40927
- return;
40928
- }
40929
- if (renderer === "openui" && !this.#downgraded) {
40930
- this.#deps.getOpenUiChannel().cancel(id);
40747
+ if (this.#inTier(this.#deps.getRenderer())) {
40748
+ this.#deps.getInteractionChannel().cancel(id);
40931
40749
  return;
40932
40750
  }
40933
40751
  this.#deps.reactBridge.cancel(id);
@@ -40947,36 +40765,11 @@ var CompositeUiBridge = class {
40947
40765
  onTextDelta(runId, delta) {
40948
40766
  return this.#deps.reactBridge.onTextDelta(runId, delta);
40949
40767
  }
40950
- /** 懒建 a2ui 桥(容器元素变化时重建);未挂载返回 undefined */
40951
- #a2uiBridge() {
40952
- const mount = this.#deps.getInteractionMount();
40953
- if (!mount) return void 0;
40954
- if (!this.#a2ui || this.#a2ui.mount !== mount) this.#a2ui = {
40955
- mount,
40956
- bridge: new LitRendererBridge({ mount })
40957
- };
40958
- return this.#a2ui.bridge;
40959
- }
40960
- async #requestA2ui(bridge, input) {
40961
- this.#deps.emit({
40962
- type: "interaction-requested",
40963
- id: input.id,
40964
- requestType: input.type
40965
- });
40966
- try {
40967
- const response = await bridge.request(input);
40968
- this.#emitResolved(input, response);
40969
- return response;
40970
- } catch (e) {
40971
- this.#emitResolved(input, {
40972
- id: input.id,
40973
- cancelled: true
40974
- });
40975
- this.#downgrade(`The A2UI renderer failed to initialize (${messageOf(e)}); falling back to native interaction cards`);
40976
- return this.#requestNative(input);
40977
- }
40768
+ /** 本档能否自行渲染交互(能力表允许且未粘性降级) */
40769
+ #inTier(renderer) {
40770
+ return renderer !== "native" && this.#capabilities[renderer].interaction && !this.#downgraded;
40978
40771
  }
40979
- async #requestOpenUi(channel, input) {
40772
+ async #requestInTier(channel, label, input) {
40980
40773
  this.#deps.emit({
40981
40774
  type: "interaction-requested",
40982
40775
  id: input.id,
@@ -40991,7 +40784,7 @@ var CompositeUiBridge = class {
40991
40784
  id: input.id,
40992
40785
  cancelled: true
40993
40786
  });
40994
- this.#downgrade(`The OpenUI renderer failed to initialize (${messageOf(e)}); falling back to native interaction cards`);
40787
+ this.#downgrade(`The ${label} renderer failed to initialize (${messageOf(e)}); falling back to native interaction cards`);
40995
40788
  return this.#requestNative(input);
40996
40789
  }
40997
40790
  }
@@ -41024,31 +40817,11 @@ var CompositeUiBridge = class {
41024
40817
  }
41025
40818
  };
41026
40819
 
41027
- //#endregion
41028
- //#region src/core/traceSink.ts
41029
- /** 写 `<chatRoot>/traces/<runId>.json`;写失败降级 console.warn,不阻断对话 */
41030
- async function writeRunTrace(fs, chatRoot, run) {
41031
- const trace = {
41032
- runId: run.id,
41033
- sessionId: run.sessionId,
41034
- startedAt: run.startedAt,
41035
- ...run.endedAt ? { endedAt: run.endedAt } : {},
41036
- status: run.status === "completed" || run.status === "cancelled" ? run.status : "failed",
41037
- activeSkills: run.activeSkillNames,
41038
- events: run.trace
41039
- };
41040
- try {
41041
- await fs.writeText(`${chatRoot}/traces/${run.id}.json`, JSON.stringify(trace, null, 2));
41042
- } catch (e) {
41043
- console.warn(`Failed to persist run trace "${run.id}": ${messageOf(e)}`);
41044
- }
41045
- }
41046
-
41047
40820
  //#endregion
41048
40821
  //#region src/core/chatEngine.ts
41049
40822
  /** 注入模型的单个附件正文上限(超出截断并标注) */
41050
40823
  const ATTACHMENT_TEXT_LIMIT = 32 * 1024;
41051
- /** 会话文件列表元数据(旧会话文件无新字段时照常读) */
40824
+ /** sdk 的 `SessionMeta` chatbot 的列表视图类型(跨层适配,非复刻) */
41052
40825
  const toSessionMeta = (session) => ({
41053
40826
  id: session.id,
41054
40827
  createdAt: session.createdAt,
@@ -41076,7 +40849,7 @@ function createDemoLlm() {
41076
40849
  return reply ?? "";
41077
40850
  };
41078
40851
  return {
41079
- complete: async () => ({ content: nextReply() }),
40852
+ complete: async () => ({ content: textParts(nextReply()) }),
41080
40853
  async *stream() {
41081
40854
  const content = nextReply();
41082
40855
  const chunkSize = Math.max(1, Math.ceil(content.length / 3));
@@ -41123,6 +40896,7 @@ function createRuntimeLlm(config) {
41123
40896
  /**
41124
40897
  * 对话框引擎:装配 WebSkillRuntime + 会话持久化 + 事件分发。
41125
40898
  * llmConfig.load() 是异步的,runtime 在首次 send 前懒装配(#ensureReady)。
40899
+ * @stable
41126
40900
  */
41127
40901
  var ChatEngine = class {
41128
40902
  bridge;
@@ -41137,24 +40911,32 @@ var ChatEngine = class {
41137
40911
  #listeners = /* @__PURE__ */ new Set();
41138
40912
  /** runId → 已激活技能累积(skill-activated 事件用;终态清理) */
41139
40913
  #runSkills = /* @__PURE__ */ new Map();
41140
- /** runId → callId → 已发出的工具事件种类(live 与 trace 重放去重;同 callId 同事件不重复发) */
41141
- #emittedToolEvents = /* @__PURE__ */ new Map();
41142
40914
  /** 当前渲染框架档(复合 uiBridge 分发;setRenderer 热切换) */
41143
40915
  #renderer;
41144
- /** a2ui 档交互容器(Chatbot ref 回调注入) */
41145
- #interactionMount;
41146
- /** openui 档交互通道(Chatbot 的 OpenUiInteraction attach 后生效) */
41147
- #openUiChannel = new OpenUiChannel();
40916
+ /** native 三档共用的交互通道(Chatbot SpecInteraction attach 后生效) */
40917
+ #interactionChannel = new SpecInteractionChannel();
41148
40918
  /** 装配时按 RuntimeConfig 应用(interaction 分发 + renderResult/streaming 透传 ReactBridgeState) */
41149
40919
  #uiBridge;
41150
40920
  /** RuntimeConfig.loop.maxHistoryMessages(装配时捕获,createSession 透传) */
41151
40921
  #maxHistoryMessages;
41152
40922
  /** Serializes OPFS draft writes so a slower prior keystroke cannot overwrite a newer value. */
41153
40923
  #surfaceDraftWrites = /* @__PURE__ */ new Map();
40924
+ /** run trace 落盘(写失败降级 warn,不阻断对话) */
40925
+ #traceStore;
40926
+ /** 会话落盘;缺省是本包内引用 `FsSessionStore` 的唯一处 */
40927
+ #sessions;
41154
40928
  constructor(adapter, options = {}) {
41155
40929
  this.#adapter = adapter;
41156
40930
  this.#options = options;
41157
40931
  this.#chatRoot = (options.chatRoot ?? "/chat").replace(/\/+$/, "");
40932
+ this.#traceStore = new FsRunTraceStore({
40933
+ root: `${this.#chatRoot}/traces`,
40934
+ fs: adapter.storage
40935
+ });
40936
+ this.#sessions = options.sessionStore ?? new FsSessionStore({
40937
+ root: `${this.#chatRoot}/sessions`,
40938
+ fs: adapter.storage
40939
+ });
41158
40940
  this.#renderer = options.renderer ?? "native";
41159
40941
  this.bridge = new ReactBridgeState({ onSurfaceDraftChange: (runId, surfaceId, value) => this.#persistSurfaceDraft(runId, surfaceId, value) });
41160
40942
  const accumulate = this.bridge.onTextDelta.bind(this.bridge);
@@ -41169,8 +40951,7 @@ var ChatEngine = class {
41169
40951
  this.#uiBridge = new CompositeUiBridge({
41170
40952
  reactBridge: this.bridge,
41171
40953
  getRenderer: () => this.#renderer,
41172
- getInteractionMount: () => this.#interactionMount,
41173
- getOpenUiChannel: () => this.#openUiChannel,
40954
+ getInteractionChannel: () => this.#interactionChannel,
41174
40955
  emit: (event) => this.#emit(event)
41175
40956
  });
41176
40957
  }
@@ -41184,13 +40965,9 @@ var ChatEngine = class {
41184
40965
  this.#renderer = renderer;
41185
40966
  this.#uiBridge.reset();
41186
40967
  }
41187
- /** a2ui 档交互容器注入(Chatbot ref 回调;卸载传 null) */
41188
- setInteractionMount(el) {
41189
- this.#interactionMount = el ?? void 0;
41190
- }
41191
- /** openui 档交互通道(Chatbot 的 OpenUiInteraction 订阅/attach;core 只产出 lang 文本) */
41192
- get openUiChannel() {
41193
- return this.#openUiChannel;
40968
+ /** native 三档共用的交互通道(Chatbot SpecInteraction 订阅/attach;core 只产出 catalog 节点树) */
40969
+ get interactionChannel() {
40970
+ return this.#interactionChannel;
41194
40971
  }
41195
40972
  /** 探测 @a2ui/lit 是否安装(Appearance/设置区 A2UI 档置灰判定) */
41196
40973
  probeA2uiAvailability() {
@@ -41217,43 +40994,48 @@ var ChatEngine = class {
41217
40994
  reloadLlm() {
41218
40995
  this.reloadConfig();
41219
40996
  }
41220
- async listSessions() {
41221
- const dir = `${this.#chatRoot}/sessions`;
41222
- if (!await this.#adapter.storage.exists(dir)) return [];
41223
- const entries = await this.#adapter.storage.list(dir);
41224
- const metas = [];
41225
- for (const entry of entries) {
41226
- if (entry.type !== "file" || !entry.path.endsWith(".json")) continue;
41227
- try {
41228
- const session = JSON.parse(await this.#adapter.storage.readText(entry.path));
41229
- metas.push(toSessionMeta(session));
41230
- } catch (e) {
41231
- console.warn(`Skipping corrupt session file "${entry.path}": ${messageOf(e)}`);
41232
- }
41233
- }
41234
- return metas.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
40997
+ /**
40998
+ * 会话列表页。归档会话也返回:UI 自己分区展示。
40999
+ * `limit` 不给默认值——缺省属于 `SessionStore` 实现,在这里兜底就等于只下推了一半。
41000
+ */
41001
+ async listSessions(options = {}) {
41002
+ const page = await this.#sessions.list({
41003
+ includeArchived: true,
41004
+ ...options
41005
+ });
41006
+ return {
41007
+ items: page.items.map(toSessionMeta),
41008
+ ...page.nextCursor !== void 0 ? { nextCursor: page.nextCursor } : {}
41009
+ };
41235
41010
  }
41236
41011
  async createSession() {
41237
- const session = {
41238
- id: newId("session"),
41239
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
41240
- messages: []
41241
- };
41242
- await this.#writeSession(session);
41012
+ const session = await this.#sessions.create({ id: newId("session") });
41243
41013
  this.#currentSessionId = session.id;
41244
41014
  return {
41245
41015
  id: session.id,
41246
41016
  createdAt: session.createdAt
41247
41017
  };
41248
41018
  }
41249
- async selectSession(id) {
41250
- const session = await this.#readSession(id);
41019
+ /**
41020
+ * 选中会话并取**最新**一页历史(页内时间升序)。
41021
+ * 返回全量 `ChatMessage[]` 的旧签名已移除:长会话下它强迫每个调用方拿全量。
41022
+ */
41023
+ async selectSession(id, options = {}) {
41024
+ const page = await this.#sessions.listMessages(id, options);
41251
41025
  this.#currentSessionId = id;
41252
- return session.messages;
41026
+ return page;
41027
+ }
41028
+ /** 向历史方向再取一页;`cursor` 只能来自上一页的 `nextCursor` */
41029
+ async loadMoreMessages(cursor, options = {}) {
41030
+ const sessionId = this.#currentSessionId;
41031
+ if (!sessionId) throw new WebSkillError("VALIDATION_FAILED", "Cannot load more messages: no active session");
41032
+ return this.#sessions.listMessages(sessionId, {
41033
+ cursor,
41034
+ ...options
41035
+ });
41253
41036
  }
41254
41037
  async deleteSession(id) {
41255
- const path = this.#sessionPath(id);
41256
- if (await this.#adapter.storage.exists(path)) await this.#adapter.storage.remove(path);
41038
+ await this.#sessions.delete(id);
41257
41039
  const attachments = `${this.attachmentsRoot}/${id}`;
41258
41040
  if (await this.#adapter.storage.exists(attachments)) await this.#adapter.storage.remove(attachments, { recursive: true });
41259
41041
  this.#handles.delete(id);
@@ -41261,36 +41043,32 @@ var ChatEngine = class {
41261
41043
  }
41262
41044
  /** 重命名会话:写入 title 并锁定(后续 send 不再用首条消息覆盖) */
41263
41045
  async renameSession(id, title) {
41264
- const session = await this.#readSession(id);
41265
- session.title = title;
41266
- session.titleLocked = true;
41267
- await this.#writeSession(session);
41046
+ await this.#sessions.setTitle(id, title, { lock: true });
41268
41047
  this.#emit({
41269
41048
  type: "session-updated",
41270
- session: toSessionMeta(session)
41049
+ session: toSessionMeta(await this.#requireSession(id))
41271
41050
  });
41272
41051
  }
41273
41052
  /** 归档会话(仅标记,消息与 trace 不动) */
41274
41053
  async archiveSession(id) {
41275
- await this.#setArchived(id, true);
41054
+ await this.#sessions.setArchived(id, true);
41055
+ this.#emit({
41056
+ type: "session-updated",
41057
+ session: toSessionMeta(await this.#requireSession(id))
41058
+ });
41276
41059
  }
41277
41060
  /** 取消归档 */
41278
41061
  async unarchiveSession(id) {
41279
- await this.#setArchived(id, false);
41280
- }
41281
- async #setArchived(id, archived) {
41282
- const session = await this.#readSession(id);
41283
- session.archived = archived;
41284
- await this.#writeSession(session);
41062
+ await this.#sessions.setArchived(id, false);
41285
41063
  this.#emit({
41286
41064
  type: "session-updated",
41287
- session: toSessionMeta(session)
41065
+ session: toSessionMeta(await this.#requireSession(id))
41288
41066
  });
41289
41067
  }
41290
41068
  /** 重发语义:取该 user 消息文本重新 send(assistant 应答新起一轮;不重写历史) */
41291
41069
  async retry(messageId) {
41292
41070
  const sessionId = this.#currentSessionId;
41293
- const message = (sessionId ? await this.#readSession(sessionId) : void 0)?.messages.find((m) => m.id === messageId);
41071
+ const message = (sessionId ? await this.#sessions.get(sessionId) : void 0)?.messages.find((m) => m.id === messageId);
41294
41072
  if (!sessionId || !message || message.role !== "user") throw new WebSkillError("VALIDATION_FAILED", `Cannot retry message "${messageId}": no matching user message in the active session`);
41295
41073
  await this.send(message.text);
41296
41074
  }
@@ -41300,23 +41078,21 @@ var ChatEngine = class {
41300
41078
  */
41301
41079
  async editAndResend(messageId, text) {
41302
41080
  const sessionId = this.#currentSessionId;
41303
- const session = sessionId ? await this.#readSession(sessionId) : void 0;
41081
+ const session = sessionId ? await this.#sessions.get(sessionId) : void 0;
41304
41082
  const index = session?.messages.findIndex((m) => m.id === messageId) ?? -1;
41305
41083
  const target = session && index >= 0 ? session.messages[index] : void 0;
41306
- if (!session || !target || target.role !== "user") throw new WebSkillError("VALIDATION_FAILED", `Cannot edit message "${messageId}": no matching user message in the active session`);
41307
- session.messages = session.messages.slice(0, index);
41308
- await this.#writeSession(session);
41084
+ if (!sessionId || !session || !target || target.role !== "user") throw new WebSkillError("VALIDATION_FAILED", `Cannot edit message "${messageId}": no matching user message in the active session`);
41085
+ await this.#sessions.replaceMessages(sessionId, session.messages.slice(0, index));
41309
41086
  await this.send(text);
41310
41087
  }
41311
41088
  /** 从当前会话消息列表删除指定消息并持久化(消息不存在/无当前会话时静默跳过) */
41312
41089
  async deleteMessage(messageId) {
41313
41090
  const sessionId = this.#currentSessionId;
41314
41091
  if (!sessionId) return;
41315
- const session = await this.#readSession(sessionId);
41092
+ const session = await this.#requireSession(sessionId);
41316
41093
  const next = session.messages.filter((m) => m.id !== messageId);
41317
41094
  if (next.length === session.messages.length) return;
41318
- session.messages = next;
41319
- await this.#writeSession(session);
41095
+ await this.#sessions.replaceMessages(sessionId, next);
41320
41096
  }
41321
41097
  /**
41322
41098
  * 驱动当前 session 的一轮 run。同 session 并发由 runtime session handle 内部
@@ -41327,7 +41103,7 @@ var ChatEngine = class {
41327
41103
  if (text.trim() === "" && attachments.length === 0) return;
41328
41104
  const runtime = await this.#ensureReady();
41329
41105
  const sessionId = this.#currentSessionId ?? (await this.createSession()).id;
41330
- const session = await this.#readSession(sessionId);
41106
+ const session = await this.#requireSession(sessionId);
41331
41107
  const prompt = await this.#withAttachments(text, attachments);
41332
41108
  const userMessage = {
41333
41109
  id: newId("msg"),
@@ -41339,9 +41115,8 @@ var ChatEngine = class {
41339
41115
  kind: "text"
41340
41116
  })) } : {}
41341
41117
  };
41342
- session.messages.push(userMessage);
41343
- if (!session.title && !session.titleLocked) session.title = text.slice(0, 30);
41344
- await this.#writeSession(session);
41118
+ await this.#sessions.appendMessages(sessionId, [userMessage]);
41119
+ if (!session.title && !session.titleLocked) await this.#sessions.setTitle(sessionId, text.slice(0, 30));
41345
41120
  this.#emit({
41346
41121
  type: "message-complete",
41347
41122
  message: userMessage
@@ -41349,7 +41124,7 @@ var ChatEngine = class {
41349
41124
  const handle = this.#sessionHandle(runtime, sessionId);
41350
41125
  try {
41351
41126
  const result = await handle.run(prompt);
41352
- await this.#finishRun(session, result);
41127
+ await this.#finishRun(sessionId, result);
41353
41128
  } catch (e) {
41354
41129
  this.#emitError(e);
41355
41130
  }
@@ -41374,7 +41149,7 @@ var ChatEngine = class {
41374
41149
  cancel(runId) {
41375
41150
  return this.#runtime?.cancel(runId) ?? false;
41376
41151
  }
41377
- /** 列出可恢复的 interrupted run(InterruptedBanner 数据源) */
41152
+ /** 列出 interrupted run(InterruptedBanner 数据源);版本不受支持的项带 unsupported 标记 */
41378
41153
  async listInterrupted() {
41379
41154
  return this.#snapshotStore().list();
41380
41155
  }
@@ -41393,8 +41168,9 @@ var ChatEngine = class {
41393
41168
  runId
41394
41169
  });
41395
41170
  const result = await runtime.resumeRun(runId);
41396
- const session = await this.#readSessionOrNew(result.run.sessionId);
41397
- await this.#finishRun(session, result);
41171
+ const sessionId = result.run.sessionId;
41172
+ if (!await this.#sessions.get(sessionId)) await this.#sessions.create({ id: sessionId });
41173
+ await this.#finishRun(sessionId, result);
41398
41174
  } catch (e) {
41399
41175
  this.#emitError(e);
41400
41176
  }
@@ -41479,6 +41255,21 @@ var ChatEngine = class {
41479
41255
  return this.#wrappedRuntimeConfig;
41480
41256
  }
41481
41257
  #wrappedRuntimeConfig;
41258
+ /**
41259
+ * 治理 port → `WebSkillRuntimeDeps` 的逐字段透传(F1)。
41260
+ * 逐字段展开而不是整体 spread:`exactOptionalPropertyTypes` 下把 `undefined`
41261
+ * 显式传给可选字段与不传不等价,而「不传」才是关闭。
41262
+ */
41263
+ #governanceDeps() {
41264
+ const g = this.#options.governance;
41265
+ if (g === void 0) return {};
41266
+ return {
41267
+ ...g.skillStateGuard !== void 0 ? { skillStateGuard: g.skillStateGuard } : {},
41268
+ ...g.skillIntegrityGuard !== void 0 ? { skillIntegrityGuard: g.skillIntegrityGuard } : {},
41269
+ ...g.skillOutcomeReporter !== void 0 ? { skillOutcomeReporter: g.skillOutcomeReporter } : {},
41270
+ ...g.catalogFilter !== void 0 ? { catalogFilter: g.catalogFilter } : {}
41271
+ };
41272
+ }
41482
41273
  async #assemble() {
41483
41274
  const fs = this.#adapter.storage;
41484
41275
  const rc = await this.#runtimeConfigStore()?.load();
@@ -41517,8 +41308,10 @@ var ChatEngine = class {
41517
41308
  root: `${this.#chatRoot}/memory`,
41518
41309
  fs
41519
41310
  }),
41311
+ ...this.#options.hooks ? { hooks: this.#applyHooksConfig(this.#options.hooks, rc) } : {},
41520
41312
  externalTools: [...pageSkillSource ? [pageSkillSource] : [], ...this.#options.generativeUi ? [createUiCatalogToolSource()] : []],
41521
41313
  skillProviders: pageSkillSource ? [pageSkillSource] : [],
41314
+ ...this.#governanceDeps(),
41522
41315
  ...Object.keys(loopConfig).length > 0 ? { config: loopConfig } : {},
41523
41316
  ...model ? { model } : {}
41524
41317
  });
@@ -41530,60 +41323,50 @@ var ChatEngine = class {
41530
41323
  runId: event.runId
41531
41324
  });
41532
41325
  }
41533
- if (event.data?.["type"] !== "tool") this.#emit({
41326
+ const toolData = event.phase === "execute" && event.data.kind === "tool" ? event.data : void 0;
41327
+ if (!toolData) this.#emit({
41534
41328
  type: "lifecycle",
41535
41329
  runId: event.runId,
41536
- phase: event.phase,
41537
41330
  at: Date.parse(event.ts),
41331
+ phase: event.phase,
41538
41332
  ...event.data ? { data: event.data } : {}
41539
41333
  });
41540
41334
  if (event.phase === "activate") {
41541
- const skillName = event.data?.["skillName"];
41542
- if (typeof skillName === "string") {
41543
- const skills = this.#runSkills.get(event.runId) ?? /* @__PURE__ */ new Set();
41544
- skills.add(skillName);
41545
- this.#runSkills.set(event.runId, skills);
41546
- this.#emit({
41547
- type: "skill-activated",
41548
- runId: event.runId,
41549
- skills: [...skills].sort()
41550
- });
41551
- }
41552
- } else if (event.phase === "execute") {
41553
- const data = event.data;
41554
- if (data?.["type"] !== "tool") return;
41555
- const status = data["status"];
41556
- const name = data["name"];
41557
- const callId = data["callId"];
41558
- if (typeof name !== "string" || typeof callId !== "string") return;
41559
- const args = typeof data["args"] === "string" ? { args: data["args"] } : {};
41560
- if (status === "started") {
41561
- if (this.#markToolEvent(event.runId, callId, "started")) this.#emit({
41562
- type: "tool-started",
41563
- runId: event.runId,
41564
- callId,
41565
- name,
41566
- ...args
41567
- });
41568
- } else if (status === "completed" || status === "failed") {
41569
- if (this.#markToolEvent(event.runId, callId, status)) this.#emit({
41570
- type: status === "completed" ? "tool-completed" : "tool-failed",
41571
- runId: event.runId,
41572
- callId,
41573
- name,
41574
- ...args
41575
- });
41576
- }
41335
+ const skills = this.#runSkills.get(event.runId) ?? /* @__PURE__ */ new Set();
41336
+ skills.add(event.data.skillName);
41337
+ this.#runSkills.set(event.runId, skills);
41338
+ this.#emit({
41339
+ type: "skill-activated",
41340
+ runId: event.runId,
41341
+ skills: [...skills].sort()
41342
+ });
41343
+ } else if (toolData) {
41344
+ const { status, callId, name } = toolData;
41345
+ const args = toolData.args === "" ? {} : { args: toolData.args };
41346
+ if (status === "started") this.#emit({
41347
+ type: "tool-started",
41348
+ runId: event.runId,
41349
+ callId,
41350
+ name,
41351
+ ...args
41352
+ });
41353
+ else this.#emit({
41354
+ type: status === "completed" ? "tool-completed" : "tool-failed",
41355
+ runId: event.runId,
41356
+ callId,
41357
+ name,
41358
+ ...args
41359
+ });
41577
41360
  } else if (event.phase === "complete" || event.phase === "fail") this.#runSkills.delete(event.runId);
41578
41361
  });
41579
41362
  this.#runtime = runtime;
41580
41363
  return runtime;
41581
41364
  }
41582
- /** run 终态公共路径(send / resume 共用):工具事件重放消息持久化 → trace 落盘 → 事件 */
41583
- async #finishRun(session, result) {
41365
+ /** run 终态公共路径(send / resume 共用):消息持久化 → trace 落盘 → 事件 */
41366
+ async #finishRun(sessionId, result) {
41584
41367
  const run = result.run;
41585
41368
  const status = run.status === "completed" || run.status === "cancelled" ? run.status : "failed";
41586
- const toolCalls = this.#replayToolEvents(run);
41369
+ const toolCalls = summarizeToolCalls(run);
41587
41370
  const latest = this.bridge.latestResult;
41588
41371
  const blocks = latest && latest.runId === run.id ? latest.blocks.filter((block) => block.type !== "markdown" || block.text.trim() !== result.output.trim()) : void 0;
41589
41372
  const surfaces = this.bridge.surfaceSnapshots.filter((snapshot) => snapshot.runId === run.id);
@@ -41598,9 +41381,8 @@ var ChatEngine = class {
41598
41381
  ...surfaces.length > 0 ? { surfaces } : {},
41599
41382
  ...toolCalls.length > 0 ? { toolCalls } : {}
41600
41383
  };
41601
- session.messages.push(assistantMessage);
41602
- await this.#writeSession(session);
41603
- await writeRunTrace(this.#adapter.storage, this.#chatRoot, run);
41384
+ await this.#sessions.appendMessages(sessionId, [assistantMessage]);
41385
+ await this.#traceStore.put(run);
41604
41386
  this.#emit({
41605
41387
  type: "run-completed",
41606
41388
  runId: run.id,
@@ -41613,67 +41395,6 @@ var ChatEngine = class {
41613
41395
  message: assistantMessage
41614
41396
  });
41615
41397
  this.#runSkills.delete(run.id);
41616
- this.#emittedToolEvents.delete(run.id);
41617
- }
41618
- /**
41619
- * 工具事件去重标记:live(runtime.events)与 trace 重放共用。
41620
- * 返回 true 表示该 (runId, callId, kind) 首次标记(应发出事件),false 表示已发过。
41621
- */
41622
- #markToolEvent(runId, callId, kind) {
41623
- let byCallId = this.#emittedToolEvents.get(runId);
41624
- if (!byCallId) {
41625
- byCallId = /* @__PURE__ */ new Map();
41626
- this.#emittedToolEvents.set(runId, byCallId);
41627
- }
41628
- let kinds = byCallId.get(callId);
41629
- if (!kinds) {
41630
- kinds = /* @__PURE__ */ new Set();
41631
- byCallId.set(callId, kinds);
41632
- }
41633
- if (kinds.has(kind)) return false;
41634
- kinds.add(kind);
41635
- return true;
41636
- }
41637
- /**
41638
- * 工具事件 trace 重放(去重兜底):live 已发出的 (callId, kind) 不重复发;
41639
- * 返回该 run 的工具调用终态序列(写入 assistant 消息,trace 为权威来源)。
41640
- */
41641
- #replayToolEvents(run) {
41642
- const toolCalls = [];
41643
- for (const event of run.trace) {
41644
- if (event.type !== "tool.started" && event.type !== "tool.completed" && event.type !== "tool.failed") continue;
41645
- const name = event.data?.["name"];
41646
- const callId = event.data?.["callId"];
41647
- if (typeof name !== "string" || typeof callId !== "string") continue;
41648
- const args = typeof event.data?.["args"] === "string" ? { args: event.data["args"] } : {};
41649
- if (event.type === "tool.started") {
41650
- if (this.#markToolEvent(run.id, callId, "started")) this.#emit({
41651
- type: "tool-started",
41652
- runId: run.id,
41653
- callId,
41654
- name,
41655
- ...args
41656
- });
41657
- } else {
41658
- const status = event.type === "tool.completed" ? "completed" : "failed";
41659
- const durationMs = typeof event.data?.["durationMs"] === "number" ? { durationMs: event.data["durationMs"] } : {};
41660
- if (this.#markToolEvent(run.id, callId, status)) this.#emit({
41661
- type: status === "completed" ? "tool-completed" : "tool-failed",
41662
- runId: run.id,
41663
- callId,
41664
- name,
41665
- ...args
41666
- });
41667
- toolCalls.push({
41668
- callId,
41669
- name,
41670
- status,
41671
- ...args,
41672
- ...durationMs
41673
- });
41674
- }
41675
- }
41676
- return toolCalls;
41677
41398
  }
41678
41399
  #snapshotStore() {
41679
41400
  return new FsRunSnapshotStore({
@@ -41681,6 +41402,14 @@ var ChatEngine = class {
41681
41402
  fs: this.#adapter.storage
41682
41403
  });
41683
41404
  }
41405
+ /** 钩子执行策略来自 console 编辑的 RuntimeConfig;钩子本身由宿主注册,UI 不碰函数 */
41406
+ #applyHooksConfig(hooks, rc) {
41407
+ if (rc) {
41408
+ hooks.timeoutMs = rc.hooks.timeoutMs;
41409
+ hooks.failOnHookError = rc.hooks.failOnHookError;
41410
+ }
41411
+ return hooks;
41412
+ }
41684
41413
  /** 每个 session 一个 runtime session 句柄(跨 run 历史延续),Map 缓存 */
41685
41414
  #sessionHandle(runtime, sessionId) {
41686
41415
  let handle = this.#handles.get(sessionId);
@@ -41693,26 +41422,10 @@ var ChatEngine = class {
41693
41422
  }
41694
41423
  return handle;
41695
41424
  }
41696
- #sessionPath(id) {
41697
- return `${this.#chatRoot}/sessions/${id}.json`;
41698
- }
41699
- async #readSession(id) {
41700
- const path = this.#sessionPath(id);
41701
- if (!await this.#adapter.storage.exists(path)) throw new WebSkillError("FS_NOT_FOUND", `Chat session "${id}" not found at ${path}`);
41702
- return JSON.parse(await this.#adapter.storage.readText(path));
41703
- }
41704
- /** resume 用:会话文件被删除时用空会话兜底(恢复不因此失败) */
41705
- async #readSessionOrNew(id) {
41706
- const path = this.#sessionPath(id);
41707
- if (await this.#adapter.storage.exists(path)) return JSON.parse(await this.#adapter.storage.readText(path));
41708
- return {
41709
- id,
41710
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
41711
- messages: []
41712
- };
41713
- }
41714
- async #writeSession(session) {
41715
- await this.#adapter.storage.writeText(this.#sessionPath(session.id), JSON.stringify(session, null, 2));
41425
+ async #requireSession(id) {
41426
+ const session = await this.#sessions.get(id);
41427
+ if (!session) throw new WebSkillError("FS_NOT_FOUND", `Chat session "${id}" not found under ${this.#chatRoot}/sessions`);
41428
+ return session;
41716
41429
  }
41717
41430
  };
41718
41431
 
@@ -41722,6 +41435,7 @@ var ChatEngine = class {
41722
41435
  * chatbot 双语字典:全部 UI 文案的单一来源(英文为主,zh 条目齐全)。
41723
41436
  * 特性区块(WelcomeScreen / SettingsDrawer 各节 / authorize 说明等)必须含
41724
41437
  * title + description 双语条目——"会说话的 UI"的文案底座。
41438
+ * @stable
41725
41439
  */
41726
41440
  const chatbotDictionary = defineDictionary({
41727
41441
  en: {
@@ -41734,6 +41448,14 @@ const chatbotDictionary = defineDictionary({
41734
41448
  "session.searchPlaceholder": "Search sessions…",
41735
41449
  "session.empty": "No conversations yet.",
41736
41450
  "session.delete": "Delete session: {title}",
41451
+ "session.rename": "Rename session: {title}",
41452
+ "session.renameInput": "New title for session: {title}",
41453
+ "session.archive": "Archive session: {title}",
41454
+ "session.unarchive": "Unarchive session: {title}",
41455
+ "session.archived": "Archived",
41456
+ "session.loadEarlier": "Load earlier sessions",
41457
+ "session.loading": "Loading…",
41458
+ "message.loadEarlier": "Load earlier messages",
41737
41459
  "welcome.title": "The official WebSkill SDK chat",
41738
41460
  "welcome.assistantTitle": "How can I help?",
41739
41461
  "welcome.description": "This chat is the reference implementation of the WebSkill SDK. Ask in natural language: the agent routes your request to installed skills, asks for missing details with native forms, and shows you every step of the run.",
@@ -41853,6 +41575,7 @@ const chatbotDictionary = defineDictionary({
41853
41575
  "vercel.preview.title": "Vercel AI SDK data contract",
41854
41576
  "vercel.preview.badge": "Preview",
41855
41577
  "vercel.preview.description": "This is the data contract for integrating the Vercel AI SDK — a payload preview, not a full runtime. The interaction renders as JSON instead of a form; cancel to end it.",
41578
+ "surface.legacy.placeholder": "Generative UI from an earlier version is no longer rendered.",
41856
41579
  "error.title": "Something went wrong",
41857
41580
  "error.suggestion": "Check the model configuration in Settings, or try again.",
41858
41581
  "error.dismiss": "Dismiss"
@@ -41867,6 +41590,14 @@ const chatbotDictionary = defineDictionary({
41867
41590
  "session.searchPlaceholder": "搜索会话…",
41868
41591
  "session.empty": "暂无会话。",
41869
41592
  "session.delete": "删除会话:{title}",
41593
+ "session.rename": "重命名会话:{title}",
41594
+ "session.renameInput": "会话新标题:{title}",
41595
+ "session.archive": "归档会话:{title}",
41596
+ "session.unarchive": "取消归档:{title}",
41597
+ "session.archived": "已归档",
41598
+ "session.loadEarlier": "加载更早的会话",
41599
+ "session.loading": "加载中…",
41600
+ "message.loadEarlier": "加载更早的消息",
41870
41601
  "welcome.title": "WebSkill SDK 官方对话框",
41871
41602
  "welcome.assistantTitle": "我可以帮你做什么?",
41872
41603
  "welcome.description": "本对话框是 WebSkill SDK 的官方参考实现。用自然语言提问:Agent 会把请求路由到已安装的技能,用原生表单补齐缺失信息,并实时展示运行的每一步。",
@@ -41986,23 +41717,18 @@ const chatbotDictionary = defineDictionary({
41986
41717
  "vercel.preview.title": "Vercel AI SDK 数据契约",
41987
41718
  "vercel.preview.badge": "预览",
41988
41719
  "vercel.preview.description": "这是接入 Vercel AI SDK 时的数据契约——payload 预览,非完整运行时。交互以 JSON 而非表单呈现;点击取消结束本次交互。",
41720
+ "surface.legacy.placeholder": "旧版本生成的界面已不再渲染。",
41989
41721
  "error.title": "出错了",
41990
41722
  "error.suggestion": "请检查设置中的模型配置,或重试。",
41991
41723
  "error.dismiss": "关闭"
41992
41724
  }
41993
41725
  });
41994
41726
  const i18n = createI18n(chatbotDictionary);
41995
- /** chatbot 组件取文案:t('welcome.title') / t('tool.summary.other', { count: 3 }) */
41727
+ /** chatbot 组件取文案:t('welcome.title') / t('tool.summary.other', { count: 3 }) @stable */
41996
41728
  const useT = i18n.useT;
41997
41729
 
41998
41730
  //#endregion
41999
41731
  //#region src/react/A2uiSurfaceHost.tsx
42000
- function usesBasicCatalog(snapshot) {
42001
- return snapshot.surface.kind === "metric" || snapshot.surface.kind === "file" && !snapshot.surface.actions?.length;
42002
- }
42003
- function specProps(snapshot) {
42004
- return snapshot.surface.props;
42005
- }
42006
41732
  /**
42007
41733
  * A2UI 回落原因(0.2.8 G2)。
42008
41734
  * 之前这里是裸 `catch {}`:缺 optional peer 与真实渲染崩溃表现完全一致——都是静默回落、
@@ -42021,17 +41747,20 @@ function fallbackReason(error) {
42021
41747
  return typeof code === "string" && code !== "" ? code : "a2ui-render-failed";
42022
41748
  }
42023
41749
  /**
42024
- * catalog 声明树经 BYOC catalog 的 Lit 元素渲染。
41750
+ * `a2ui` 档:catalog 声明树经 BYOC catalog 的 Lit 元素渲染。
42025
41751
  * 元素与协议里公布的 `catalog_definition` 派生自同一份 shape,因此这里
42026
41752
  * 不需要第二套映射;缺 optional peer 时回落到 native 渲染而不是留白。
41753
+ *
41754
+ * 交互请求(路径 A)与模型产出的 surface(路径 B)共用本组件——这是 0.4.0 B4
41755
+ * 「同一档下命中同一个渲染入口」的落点,`data-testid` 因此对两条路径一致。
42027
41756
  */
42028
- function A2uiSpecSurface({ snapshot, onAction }) {
41757
+ function A2uiSpecSurface({ spec, surfaceId, actions, onAction, registry, runId, draft, onDraftChange }) {
42029
41758
  const mountRef = useRef(null);
42030
41759
  const [fallback, setFallback] = useState();
42031
41760
  const emit = useEffectEvent((actionId, value) => {
42032
- const declared = ((snapshot.surface.kind === "custom" ? snapshot.surface.actions : void 0) ?? []).find((action) => action.id === actionId);
41761
+ const declared = (actions ?? []).find((action) => action.id === actionId);
42033
41762
  onAction?.({
42034
- surfaceId: snapshot.surface.id,
41763
+ surfaceId,
42035
41764
  actionId,
42036
41765
  intent: declared?.intent ?? "select",
42037
41766
  ...declared?.nonce ? { nonce: declared.nonce } : {},
@@ -42041,7 +41770,7 @@ function A2uiSpecSurface({ snapshot, onAction }) {
42041
41770
  useEffect(() => {
42042
41771
  let disposed = false;
42043
41772
  let surfaceElement;
42044
- const surfaceId = `webskill-spec-${snapshot.surface.id}`;
41773
+ const a2uiSurfaceId = `webskill-spec-${surfaceId}`;
42045
41774
  (async () => {
42046
41775
  try {
42047
41776
  const [{ MessageProcessor }, lit, catalog] = await Promise.all([
@@ -42052,10 +41781,11 @@ function A2uiSpecSurface({ snapshot, onAction }) {
42052
41781
  lit.A2uiSurface;
42053
41782
  const mount = mountRef.current;
42054
41783
  if (disposed || !mount) return;
41784
+ configureA2uiMarkdown(mount);
42055
41785
  const processor = new MessageProcessor([catalog, lit.basicCatalog], (action) => {
42056
41786
  const decoded = fromA2uiSpecAction(action);
42057
41787
  if (!decoded) return;
42058
- const values = processor.getClientDataModel("v0.9.1")?.surfaces?.[surfaceId]?.[A2UI_SPEC_FORM_PATH.slice(1)];
41788
+ const values = processor.getClientDataModel("v0.9.1")?.surfaces?.[a2uiSurfaceId]?.[A2UI_SPEC_FORM_PATH.slice(1)];
42059
41789
  emit(decoded.actionId, values ?? {});
42060
41790
  }, { version: "v0.9.1" });
42061
41791
  processor.onSurfaceCreated((surface) => {
@@ -42064,58 +41794,9 @@ function A2uiSpecSurface({ snapshot, onAction }) {
42064
41794
  surfaceElement.surface = surface;
42065
41795
  mount.replaceChildren(surfaceElement);
42066
41796
  });
42067
- processor.processMessages(toA2uiSpecMessages(uiCatalog, specProps(snapshot).spec, { surfaceId }));
42068
- } catch (error) {
42069
- reportA2uiFallback(snapshot.surface.id, "spec", error);
42070
- if (!disposed) setFallback(fallbackReason(error));
42071
- }
42072
- })();
42073
- return () => {
42074
- disposed = true;
42075
- surfaceElement?.remove();
42076
- };
42077
- }, [snapshot]);
42078
- if (fallback) {
42079
- const surface = snapshot.surface;
42080
- return /* @__PURE__ */ jsx("div", {
42081
- "data-testid": "a2ui-spec-surface",
42082
- "data-renderer": "native-fallback",
42083
- "data-fallback-reason": fallback,
42084
- children: /* @__PURE__ */ jsx(NativeSpecSurface, {
42085
- surfaceId: surface.id,
42086
- spec: specProps(snapshot).spec,
42087
- actions: surface.actions ?? [],
42088
- ...onAction ? { onAction } : {}
42089
- })
42090
- });
42091
- }
42092
- return /* @__PURE__ */ jsx("div", {
42093
- ref: mountRef,
42094
- "data-testid": "a2ui-spec-surface",
42095
- "data-webskill-provenance": "a2ui-catalog"
42096
- });
42097
- }
42098
- function A2uiStandardSurface({ snapshot, registry }) {
42099
- const mountRef = useRef(null);
42100
- const [fallback, setFallback] = useState();
42101
- useEffect(() => {
42102
- let disposed = false;
42103
- let surfaceElement;
42104
- (async () => {
42105
- try {
42106
- const [{ MessageProcessor }, { A2uiSurface, basicCatalog }] = await Promise.all([import("@a2ui/web_core/v0_9"), import("@a2ui/lit/v0_9")]);
42107
- const mount = mountRef.current;
42108
- if (disposed || !mount) return;
42109
- const processor = new MessageProcessor([basicCatalog], () => void 0, { version: "v0.9.1" });
42110
- processor.onSurfaceCreated((surface) => {
42111
- if (disposed) return;
42112
- surfaceElement = mount.ownerDocument.createElement("a2ui-surface");
42113
- surfaceElement.surface = surface;
42114
- mount.replaceChildren(surfaceElement);
42115
- });
42116
- processor.processMessages(toA2uiSurfaceMessages(snapshot));
41797
+ processor.processMessages(toA2uiSpecMessages(uiCatalog, spec, { surfaceId: a2uiSurfaceId }));
42117
41798
  } catch (error) {
42118
- reportA2uiFallback(snapshot.surface.id, "standard", error);
41799
+ reportA2uiFallback(surfaceId, "spec", error);
42119
41800
  if (!disposed) setFallback(fallbackReason(error));
42120
41801
  }
42121
41802
  })();
@@ -42123,65 +41804,52 @@ function A2uiStandardSurface({ snapshot, registry }) {
42123
41804
  disposed = true;
42124
41805
  surfaceElement?.remove();
42125
41806
  };
42126
- }, [snapshot]);
41807
+ }, [spec, surfaceId]);
42127
41808
  if (fallback) return /* @__PURE__ */ jsx("div", {
42128
- "data-testid": "a2ui-standard-surface",
41809
+ "data-testid": "a2ui-spec-surface",
42129
41810
  "data-renderer": "native-fallback",
42130
41811
  "data-fallback-reason": fallback,
42131
- children: /* @__PURE__ */ jsx(UiSurfaceSnapshotList, {
42132
- snapshots: [snapshot],
42133
- ...registry ? { registry } : {}
41812
+ children: /* @__PURE__ */ jsx(NativeSpecSurface, {
41813
+ surfaceId,
41814
+ spec,
41815
+ actions: actions ?? [],
41816
+ ...registry ? { registry } : {},
41817
+ ...runId ? { runId } : {},
41818
+ ...draft ? { draft } : {},
41819
+ ...onAction ? { onAction } : {},
41820
+ ...onDraftChange ? { onDraftChange } : {}
42134
41821
  })
42135
41822
  });
42136
41823
  return /* @__PURE__ */ jsx("div", {
42137
41824
  ref: mountRef,
42138
- "data-testid": "a2ui-standard-surface",
42139
- "data-webskill-provenance": "a2ui-standard"
41825
+ "data-testid": "a2ui-spec-surface",
41826
+ "data-webskill-provenance": "a2ui-catalog"
42140
41827
  });
42141
41828
  }
42142
- /** Renders persisted A2UI Basic Catalog surfaces and controlled WebSkill extensions without a live bridge. @experimental */
41829
+ /** Renders persisted WebSkill UI spec snapshots through the A2UI BYOC catalog. @experimental */
42143
41830
  function A2uiSurfaceSnapshotHost({ snapshots, registry, onAction, drafts, onDraftChange }) {
42144
- const specSnapshots = snapshots.filter((snapshot) => isUiSpecSurface(snapshot.surface));
42145
- const rest = snapshots.filter((snapshot) => !isUiSpecSurface(snapshot.surface));
42146
- const standardSnapshots = rest.filter(usesBasicCatalog);
42147
- const extensionSnapshots = rest.filter((snapshot) => !usesBasicCatalog(snapshot));
42148
41831
  if (snapshots.length === 0) return null;
42149
- return /* @__PURE__ */ jsxs("div", {
41832
+ return /* @__PURE__ */ jsx("div", {
42150
41833
  "data-testid": "a2ui-surface-host",
42151
- children: [
42152
- specSnapshots.map((snapshot) => /* @__PURE__ */ jsx(A2uiSpecSurface, {
42153
- snapshot,
42154
- onAction
42155
- }, snapshot.surface.id)),
42156
- standardSnapshots.map((snapshot) => /* @__PURE__ */ jsx(A2uiStandardSurface, {
42157
- snapshot,
42158
- ...registry ? { registry } : {}
42159
- }, snapshot.surface.id)),
42160
- extensionSnapshots.length > 0 ? /* @__PURE__ */ jsxs("div", {
42161
- "data-testid": "a2ui-surface-extension",
42162
- "data-webskill-provenance": "a2ui-extension",
42163
- children: [/* @__PURE__ */ jsx("span", {
42164
- className: "sr-only",
42165
- children: "WebSkill extension"
42166
- }), /* @__PURE__ */ jsx(UiSurfaceSnapshotList, {
42167
- snapshots: extensionSnapshots,
42168
- ...registry ? { registry } : {},
42169
- ...onAction ? { onAction } : {},
42170
- ...drafts ? { drafts } : {},
42171
- ...onDraftChange ? { onDraftChange } : {}
42172
- })]
42173
- }) : null
42174
- ]
41834
+ children: snapshots.map((snapshot) => /* @__PURE__ */ jsx(A2uiSpecSurface, {
41835
+ surfaceId: snapshot.id,
41836
+ spec: snapshot.node,
41837
+ actions: snapshot.actions ?? [],
41838
+ registry,
41839
+ onAction,
41840
+ runId: snapshot.runId,
41841
+ draft: drafts?.[snapshot.id],
41842
+ onDraftChange
41843
+ }, snapshot.id))
42175
41844
  });
42176
41845
  }
42177
- /** Renders A2UI Basic Catalog surfaces and controlled WebSkill extensions from live bridge snapshots. @experimental */
41846
+ /** Renders WebSkill UI spec snapshots from live bridge state through the A2UI BYOC catalog. @experimental */
42178
41847
  function A2uiSurfaceHost({ bridge, runId, registry }) {
42179
41848
  const snapshots = useSyncExternalStore(bridge.subscribe, () => bridge.surfaceSnapshots);
42180
41849
  const visibleSnapshots = runId === void 0 ? snapshots : snapshots.filter((snapshot) => snapshot.runId === runId);
42181
- const actionableSnapshots = visibleSnapshots.filter((snapshot) => isUiSpecSurface(snapshot.surface) || !usesBasicCatalog(snapshot));
42182
41850
  const handleAction = (event) => {
42183
- const snapshot = actionableSnapshots.find((candidate) => candidate.surface.id === event.surfaceId);
42184
- const action = snapshot && "actions" in snapshot.surface ? snapshot.surface.actions?.find((candidate) => candidate.id === event.actionId) : void 0;
41851
+ const snapshot = visibleSnapshots.find((candidate) => candidate.id === event.surfaceId);
41852
+ const action = snapshot?.actions?.find((candidate) => candidate.id === event.actionId);
42185
41853
  if (!snapshot || !action) return;
42186
41854
  const decoded = fromA2uiSurfaceAction(toA2uiSurfaceAction(snapshot, action, event.value));
42187
41855
  if (!decoded?.runId || !decoded.nonce) return;
@@ -42362,7 +42030,7 @@ function lifecyclePart(step) {
42362
42030
  phase: step.phase,
42363
42031
  at: step.at,
42364
42032
  ...step.endedAt !== void 0 ? { endedAt: step.endedAt } : {},
42365
- ...step.data ? { detail: step.data } : {}
42033
+ ...step.note !== void 0 ? { note: step.note } : {}
42366
42034
  }
42367
42035
  };
42368
42036
  }
@@ -42557,7 +42225,7 @@ function useWebSkillAui({ messages, live, sessions, currentSessionId, sessionsLo
42557
42225
  //#region src/react/AssistantUiShell.tsx
42558
42226
  /**
42559
42227
  * assistant-ui only owns the React thread primitives. WebSkill remains the
42560
- * source of truth for messages, sessions, Runtime actions, and UiSurface data.
42228
+ * source of truth for messages, sessions, Runtime actions, and UI spec surface data.
42561
42229
  */
42562
42230
  function AssistantUiShellProvider({ children, ...props }) {
42563
42231
  return /* @__PURE__ */ jsx(AuiProvider, {
@@ -42568,15 +42236,23 @@ function AssistantUiShellProvider({ children, ...props }) {
42568
42236
  children: /* @__PURE__ */ jsx(ThreadPrimitive.ViewportProvider, { children })
42569
42237
  });
42570
42238
  }
42571
- function AssistantUiMessageList({ messages, renderMessage }) {
42239
+ function AssistantUiMessageList({ messages, earlier, renderMessage }) {
42240
+ const t = useT();
42572
42241
  const byId = useMemo(() => new Map(messages.map((message) => [message.id, message])), [messages]);
42573
- return /* @__PURE__ */ jsx(ThreadPrimitive.Root, {
42242
+ return /* @__PURE__ */ jsxs(ThreadPrimitive.Root, {
42574
42243
  "data-testid": "assistant-ui-thread",
42575
42244
  className: "flex w-full min-w-0 flex-col gap-4",
42576
- children: /* @__PURE__ */ jsx(ThreadPrimitive.Messages, { children: ({ message }) => /* @__PURE__ */ jsx(MessagePrimitive.Root, {
42245
+ children: [earlier ? /* @__PURE__ */ jsx("button", {
42246
+ type: "button",
42247
+ "data-testid": "load-earlier-messages",
42248
+ disabled: earlier.loading,
42249
+ onClick: earlier.onLoad,
42250
+ className: "self-center rounded-md border border-border px-3 py-1.5 text-sm text-muted transition-colors hover:bg-subtle hover:text-ink disabled:opacity-60",
42251
+ children: earlier.loading ? t("session.loading") : t("message.loadEarlier")
42252
+ }) : null, /* @__PURE__ */ jsx(ThreadPrimitive.Messages, { children: ({ message }) => /* @__PURE__ */ jsx(MessagePrimitive.Root, {
42577
42253
  "data-webskill-message-role": message.role,
42578
42254
  children: renderMessage(byId.get(message.id))
42579
- }) })
42255
+ }) })]
42580
42256
  });
42581
42257
  }
42582
42258
  /** Uses assistant-ui's scroll and accessibility viewport for the controlled external thread. */
@@ -42666,12 +42342,85 @@ function AssistantUiComposer({ running, disabled = false, placeholder, attachmen
42666
42342
  })
42667
42343
  });
42668
42344
  }
42345
+ /**
42346
+ * 一行会话。归档区与常规区共用:两边只差 archive/unarchive 一个按钮,
42347
+ * 各写一份迟早会漂。重命名走 `aui.threads.item()`——assistant-ui 没有 Rename primitive,
42348
+ * 但 threads 客户端有 `rename` 方法(ChatEngine.renameSession 的入口)。
42349
+ */
42350
+ function SessionRow({ id, title, active, archived, renaming, onNavigate, onStartRename, onEndRename }) {
42351
+ const t = useT();
42352
+ const aui = useAui();
42353
+ const iconButton = "inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 hover:bg-subtle hover:text-ink";
42354
+ const commitRename = (next) => {
42355
+ const trimmed = next.trim();
42356
+ if (trimmed !== "" && trimmed !== title) aui.threads.item({ id }).rename(trimmed);
42357
+ onEndRename();
42358
+ };
42359
+ return /* @__PURE__ */ jsxs(ThreadListItemPrimitive.Root, {
42360
+ role: "listitem",
42361
+ "data-webskill-session-archived": archived ? "true" : void 0,
42362
+ className: "group flex items-center gap-1",
42363
+ children: [renaming ? /* @__PURE__ */ jsx("input", {
42364
+ autoFocus: true,
42365
+ "aria-label": t("session.renameInput", { title }),
42366
+ defaultValue: title,
42367
+ onBlur: (event) => commitRename(event.target.value),
42368
+ onKeyDown: (event) => {
42369
+ if (event.key === "Enter") commitRename(event.currentTarget.value);
42370
+ if (event.key === "Escape") onEndRename();
42371
+ },
42372
+ className: "min-w-0 flex-1 rounded-md border border-input bg-background px-2.5 py-2 text-sm text-ink outline-none focus:border-ring"
42373
+ }) : /* @__PURE__ */ jsx(ThreadListItemPrimitive.Trigger, {
42374
+ "data-webskill-thread-id": id,
42375
+ "aria-current": active ? "page" : void 0,
42376
+ onClick: onNavigate,
42377
+ className: `min-w-0 flex-1 truncate rounded-md px-2.5 py-2 text-left text-sm transition-colors ${active ? "bg-subtle font-medium text-ink" : "text-muted hover:bg-subtle hover:text-ink"}`,
42378
+ children: /* @__PURE__ */ jsx(ThreadListItemPrimitive.Title, { fallback: id })
42379
+ }), renaming ? null : /* @__PURE__ */ jsxs(Fragment$1, { children: [
42380
+ /* @__PURE__ */ jsx("button", {
42381
+ type: "button",
42382
+ "aria-label": t("session.rename", { title }),
42383
+ onClick: onStartRename,
42384
+ className: iconButton,
42385
+ children: /* @__PURE__ */ jsx(Pencil, {
42386
+ className: "size-3.5",
42387
+ "aria-hidden": true
42388
+ })
42389
+ }),
42390
+ archived ? /* @__PURE__ */ jsx(ThreadListItemPrimitive.Unarchive, {
42391
+ "aria-label": t("session.unarchive", { title }),
42392
+ className: iconButton,
42393
+ children: /* @__PURE__ */ jsx(ArchiveRestore, {
42394
+ className: "size-3.5",
42395
+ "aria-hidden": true
42396
+ })
42397
+ }) : /* @__PURE__ */ jsx(ThreadListItemPrimitive.Archive, {
42398
+ "aria-label": t("session.archive", { title }),
42399
+ className: iconButton,
42400
+ children: /* @__PURE__ */ jsx(Archive, {
42401
+ className: "size-3.5",
42402
+ "aria-hidden": true
42403
+ })
42404
+ }),
42405
+ /* @__PURE__ */ jsx(ThreadListItemPrimitive.Delete, {
42406
+ "aria-label": t("session.delete", { title }),
42407
+ className: `${iconButton} hover:bg-danger-soft hover:text-danger`,
42408
+ children: /* @__PURE__ */ jsx(Trash2, {
42409
+ className: "size-3.5",
42410
+ "aria-hidden": true
42411
+ })
42412
+ })
42413
+ ] })]
42414
+ });
42415
+ }
42669
42416
  /** Sessions are read through the assistant-ui threads client, which ChatEngine drives. */
42670
- function AssistantUiSessionList({ className = "", onNavigate }) {
42417
+ function AssistantUiSessionList({ className = "", onNavigate, hasEarlier = false, loadingEarlier = false, onLoadEarlier }) {
42671
42418
  const t = useT();
42672
42419
  const [query, setQuery] = useState("");
42420
+ const [renamingId, setRenamingId] = useState();
42673
42421
  const normalizedQuery = query.trim().toLocaleLowerCase();
42674
42422
  const threadIds = useAuiState((s) => s.threads.threadIds);
42423
+ const archivedThreadIds = useAuiState((s) => s.threads.archivedThreadIds);
42675
42424
  const threadItems = useAuiState((s) => s.threads.threadItems);
42676
42425
  const mainThreadId = useAuiState((s) => s.threads.mainThreadId);
42677
42426
  const matches = useMemo(() => {
@@ -42681,6 +42430,7 @@ function AssistantUiSessionList({ className = "", onNavigate }) {
42681
42430
  };
42682
42431
  }, [threadItems, normalizedQuery]);
42683
42432
  const visibleCount = threadIds.filter(matches).length;
42433
+ const visibleArchivedCount = archivedThreadIds.filter(matches).length;
42684
42434
  return /* @__PURE__ */ jsxs(ThreadListPrimitive.Root, {
42685
42435
  "data-testid": "assistant-ui-thread-list",
42686
42436
  "aria-label": t("session.title"),
@@ -42716,32 +42466,53 @@ function AssistantUiSessionList({ className = "", onNavigate }) {
42716
42466
  /* @__PURE__ */ jsxs("div", {
42717
42467
  className: "min-h-0 flex-1 overflow-y-auto px-2 pb-3",
42718
42468
  role: "list",
42719
- children: [visibleCount === 0 ? /* @__PURE__ */ jsx("p", {
42720
- className: "px-2 py-4 text-sm text-muted",
42721
- children: t("session.empty")
42722
- }) : null, /* @__PURE__ */ jsx(ThreadListPrimitive.Items, { children: ({ threadListItem }) => {
42723
- if (!matches(threadListItem.id)) return null;
42724
- const title = threadListItem.title ?? threadListItem.id;
42725
- const active = threadListItem.id === mainThreadId;
42726
- return /* @__PURE__ */ jsxs(ThreadListItemPrimitive.Root, {
42727
- role: "listitem",
42728
- className: "group flex items-center gap-1",
42729
- children: [/* @__PURE__ */ jsx(ThreadListItemPrimitive.Trigger, {
42730
- "data-webskill-thread-id": threadListItem.id,
42731
- "aria-current": active ? "page" : void 0,
42732
- onClick: onNavigate,
42733
- className: `min-w-0 flex-1 truncate rounded-md px-2.5 py-2 text-left text-sm transition-colors ${active ? "bg-subtle font-medium text-ink" : "text-muted hover:bg-subtle hover:text-ink"}`,
42734
- children: /* @__PURE__ */ jsx(ThreadListItemPrimitive.Title, { fallback: threadListItem.id })
42735
- }), /* @__PURE__ */ jsx(ThreadListItemPrimitive.Delete, {
42736
- "aria-label": t("session.delete", { title }),
42737
- className: "inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 hover:bg-danger-soft hover:text-danger",
42738
- children: /* @__PURE__ */ jsx(Trash2, {
42739
- className: "size-3.5",
42740
- "aria-hidden": true
42741
- })
42742
- })]
42743
- });
42744
- } })]
42469
+ children: [
42470
+ hasEarlier && onLoadEarlier ? /* @__PURE__ */ jsx("button", {
42471
+ type: "button",
42472
+ "data-testid": "load-earlier-sessions",
42473
+ disabled: loadingEarlier,
42474
+ onClick: onLoadEarlier,
42475
+ className: "mb-1 w-full rounded-md px-2.5 py-2 text-sm text-muted transition-colors hover:bg-subtle hover:text-ink disabled:opacity-60",
42476
+ children: loadingEarlier ? t("session.loading") : t("session.loadEarlier")
42477
+ }) : null,
42478
+ visibleCount === 0 ? /* @__PURE__ */ jsx("p", {
42479
+ className: "px-2 py-4 text-sm text-muted",
42480
+ children: t("session.empty")
42481
+ }) : null,
42482
+ /* @__PURE__ */ jsx(ThreadListPrimitive.Items, { children: ({ threadListItem }) => {
42483
+ if (!matches(threadListItem.id)) return null;
42484
+ return /* @__PURE__ */ jsx(SessionRow, {
42485
+ id: threadListItem.id,
42486
+ title: threadListItem.title ?? threadListItem.id,
42487
+ active: threadListItem.id === mainThreadId,
42488
+ archived: false,
42489
+ renaming: renamingId === threadListItem.id,
42490
+ onNavigate,
42491
+ onStartRename: () => setRenamingId(threadListItem.id),
42492
+ onEndRename: () => setRenamingId(void 0)
42493
+ });
42494
+ } }),
42495
+ visibleArchivedCount > 0 ? /* @__PURE__ */ jsx("p", {
42496
+ className: "px-2.5 pb-1 pt-4 text-xs font-medium uppercase tracking-wide text-muted",
42497
+ children: t("session.archived")
42498
+ }) : null,
42499
+ /* @__PURE__ */ jsx(ThreadListPrimitive.Items, {
42500
+ archived: true,
42501
+ children: ({ threadListItem }) => {
42502
+ if (!matches(threadListItem.id)) return null;
42503
+ return /* @__PURE__ */ jsx(SessionRow, {
42504
+ id: threadListItem.id,
42505
+ title: threadListItem.title ?? threadListItem.id,
42506
+ active: threadListItem.id === mainThreadId,
42507
+ archived: true,
42508
+ renaming: renamingId === threadListItem.id,
42509
+ onNavigate,
42510
+ onStartRename: () => setRenamingId(threadListItem.id),
42511
+ onEndRename: () => setRenamingId(void 0)
42512
+ });
42513
+ }
42514
+ })
42515
+ ]
42745
42516
  })
42746
42517
  ]
42747
42518
  });
@@ -42921,8 +42692,7 @@ function LifecycleStep({ data }) {
42921
42692
  const at = typeof data["at"] === "number" ? data["at"] : void 0;
42922
42693
  const endedAt = typeof data["endedAt"] === "number" ? data["endedAt"] : void 0;
42923
42694
  const duration = at !== void 0 && endedAt !== void 0 ? formatDuration(endedAt - at) : void 0;
42924
- const detail = data["detail"];
42925
- const note = readString(detail, "strategy") ?? readString(detail, "type") ?? readString(detail, "reason");
42695
+ const note = readString(data, "note");
42926
42696
  const label = t(key);
42927
42697
  return /* @__PURE__ */ jsxs("li", {
42928
42698
  "aria-label": duration ? `${label}, ${duration}` : label,
@@ -43094,279 +42864,18 @@ function WebSkillMessageParts() {
43094
42864
  });
43095
42865
  }
43096
42866
 
43097
- //#endregion
43098
- //#region src/react/OpenUiInteraction.tsx
43099
- const inputClass = "w-full rounded-lg border border-border bg-surface px-3 py-2 text-sm text-ink transition-colors placeholder:text-muted hover:border-ring";
43100
- /**
43101
- * openui-lang 组件词汇的 ui-kit 视觉实现(与 @webskill/ui toOpenUiLang 的约定一致:
43102
- * Form/Text/TextField/NumberField/BooleanField/SelectField/SubmitButton/CancelButton)。
43103
- * 视觉对齐 native InteractionCard 的表单样式。
43104
- */
43105
- function buildOpenUiLibrary(openui, z, options = {}) {
43106
- const { defineComponent, createLibrary, useStateField, useTriggerAction } = openui;
43107
- const requiredMark = (required) => required ? /* @__PURE__ */ jsx("span", {
43108
- className: "text-danger",
43109
- children: " *"
43110
- }) : null;
43111
- const TextField = defineComponent({
43112
- name: "TextField",
43113
- description: "text input",
43114
- props: z.object({
43115
- name: z.string(),
43116
- label: z.string().optional(),
43117
- required: z.boolean().optional(),
43118
- defaultValue: z.string().nullable().optional()
43119
- }),
43120
- component: function TextFieldRenderer({ props }) {
43121
- const field = useStateField(props.name, props.defaultValue ?? "");
43122
- return /* @__PURE__ */ jsxs("label", {
43123
- className: "flex flex-col gap-1 text-sm text-ink",
43124
- children: [/* @__PURE__ */ jsxs("span", { children: [props.label ?? props.name, requiredMark(props.required)] }), /* @__PURE__ */ jsx("input", {
43125
- className: inputClass,
43126
- value: String(field.value ?? ""),
43127
- onChange: (e) => field.setValue(e.target.value)
43128
- })]
43129
- });
43130
- }
43131
- });
43132
- const NumberField = defineComponent({
43133
- name: "NumberField",
43134
- description: "number input",
43135
- props: z.object({
43136
- name: z.string(),
43137
- label: z.string().optional(),
43138
- required: z.boolean().optional()
43139
- }),
43140
- component: function NumberFieldRenderer({ props }) {
43141
- const field = useStateField(props.name, null);
43142
- return /* @__PURE__ */ jsxs("label", {
43143
- className: "flex flex-col gap-1 text-sm text-ink",
43144
- children: [/* @__PURE__ */ jsxs("span", { children: [props.label ?? props.name, requiredMark(props.required)] }), /* @__PURE__ */ jsx("input", {
43145
- type: "number",
43146
- className: inputClass,
43147
- value: field.value === void 0 || field.value === null ? "" : String(field.value),
43148
- onChange: (e) => field.setValue(e.target.value === "" ? void 0 : Number(e.target.value))
43149
- })]
43150
- });
43151
- }
43152
- });
43153
- const BooleanField = defineComponent({
43154
- name: "BooleanField",
43155
- description: "checkbox",
43156
- props: z.object({
43157
- name: z.string(),
43158
- label: z.string().optional(),
43159
- defaultValue: z.boolean().optional()
43160
- }),
43161
- component: function BooleanFieldRenderer({ props }) {
43162
- const field = useStateField(props.name, props.defaultValue ?? false);
43163
- return /* @__PURE__ */ jsxs("label", {
43164
- className: "flex items-center gap-2 text-sm text-ink",
43165
- children: [/* @__PURE__ */ jsx("input", {
43166
- type: "checkbox",
43167
- className: "size-4 accent-primary",
43168
- checked: field.value === true,
43169
- onChange: (e) => field.setValue(e.target.checked)
43170
- }), /* @__PURE__ */ jsx("span", { children: props.label ?? props.name })]
43171
- });
43172
- }
43173
- });
43174
- const SelectField = defineComponent({
43175
- name: "SelectField",
43176
- description: "select input",
43177
- props: z.object({
43178
- name: z.string(),
43179
- label: z.string().optional(),
43180
- options: z.array(z.object({
43181
- label: z.string(),
43182
- value: z.unknown()
43183
- }))
43184
- }),
43185
- component: function SelectFieldRenderer({ props }) {
43186
- const field = useStateField(props.name, props.options[0]?.value ?? "");
43187
- return /* @__PURE__ */ jsxs("label", {
43188
- className: "flex flex-col gap-1 text-sm text-ink",
43189
- children: [/* @__PURE__ */ jsx("span", { children: props.label ?? props.name }), /* @__PURE__ */ jsx("select", {
43190
- className: inputClass,
43191
- value: JSON.stringify(field.value ?? ""),
43192
- onChange: (e) => field.setValue(JSON.parse(e.target.value)),
43193
- children: props.options.map((option, i) => /* @__PURE__ */ jsx("option", {
43194
- value: JSON.stringify(option.value),
43195
- children: option.label
43196
- }, i))
43197
- })]
43198
- });
43199
- }
43200
- });
43201
- const SubmitButton = defineComponent({
43202
- name: "SubmitButton",
43203
- description: "submit button",
43204
- props: z.object({
43205
- label: z.string(),
43206
- actionType: z.string(),
43207
- requestId: z.string()
43208
- }),
43209
- component: function SubmitButtonRenderer({ props }) {
43210
- const trigger = useTriggerAction();
43211
- return /* @__PURE__ */ jsx(Button, {
43212
- onClick: () => void trigger(props.label, void 0, {
43213
- type: props.actionType,
43214
- params: { id: props.requestId }
43215
- }),
43216
- children: props.label
43217
- });
43218
- }
43219
- });
43220
- const CancelButton = defineComponent({
43221
- name: "CancelButton",
43222
- description: "cancel button",
43223
- props: z.object({
43224
- label: z.string(),
43225
- actionType: z.string(),
43226
- requestId: z.string()
43227
- }),
43228
- component: function CancelButtonRenderer({ props }) {
43229
- const trigger = useTriggerAction();
43230
- return /* @__PURE__ */ jsx(Button, {
43231
- variant: "secondary",
43232
- onClick: () => void trigger(props.label, void 0, {
43233
- type: props.actionType,
43234
- params: { id: props.requestId }
43235
- }),
43236
- children: props.label
43237
- });
43238
- }
43239
- });
43240
- const Text = defineComponent({
43241
- name: "Text",
43242
- description: "plain display text",
43243
- props: z.object({ content: z.string() }),
43244
- component: function TextRenderer({ props }) {
43245
- return /* @__PURE__ */ jsx("p", {
43246
- className: "text-sm break-words text-muted",
43247
- children: props.content
43248
- });
43249
- }
43250
- });
43251
- const components = [
43252
- defineComponent({
43253
- name: "Form",
43254
- description: "form container",
43255
- props: z.object({
43256
- title: z.string().nullable().optional(),
43257
- children: z.array(z.any()).optional()
43258
- }),
43259
- component: function FormRenderer({ props, renderNode }) {
43260
- return /* @__PURE__ */ jsxs("form", {
43261
- className: "flex flex-col gap-3",
43262
- onSubmit: (e) => {
43263
- e.preventDefault();
43264
- },
43265
- children: [props.title ? /* @__PURE__ */ jsx("h3", {
43266
- className: "font-display text-sm font-semibold text-ink",
43267
- children: props.title
43268
- }) : null, (props.children ?? []).map((child, i) => /* @__PURE__ */ jsx("div", { children: renderNode(child) }, i))]
43269
- });
43270
- }
43271
- }),
43272
- Text,
43273
- TextField,
43274
- NumberField,
43275
- BooleanField,
43276
- SelectField,
43277
- SubmitButton,
43278
- CancelButton
43279
- ];
43280
- if (options.onSurfaceAction) {
43281
- const WebSkillSurfaceHost = defineComponent({
43282
- name: "WebSkillSurfaceHost",
43283
- description: "controlled WebSkill structured UI surface",
43284
- props: z.object({ descriptor: z.any() }),
43285
- component: function WebSkillSurfaceHostRenderer({ props }) {
43286
- if (props.descriptor?.type !== "webskill-surface") return null;
43287
- return /* @__PURE__ */ jsx("div", {
43288
- "data-webskill-provenance": props.descriptor.provenance,
43289
- children: /* @__PURE__ */ jsx(UiSurfaceSnapshotList, {
43290
- snapshots: [props.descriptor.snapshot],
43291
- ...options.registry ? { registry: options.registry } : {},
43292
- onAction: options.onSurfaceAction,
43293
- ...options.drafts ? { drafts: options.drafts } : {},
43294
- ...options.onSurfaceDraftChange ? { onDraftChange: options.onSurfaceDraftChange } : {}
43295
- })
43296
- });
43297
- }
43298
- });
43299
- components.push(WebSkillSurfaceHost);
43300
- }
43301
- return createLibrary({
43302
- components,
43303
- root: options.root ?? "Form"
43304
- });
43305
- }
43306
- /**
43307
- * openui 档交互视图:订阅 OpenUiChannel(core 产出 openui-lang 文本),动态加载
43308
- * optional peer @openuidev/react-lang 的 Renderer 渲染;动作事件回传 channel.handleAction。
43309
- * 模块加载失败 → channel.fail(桥粘性降级 native,InteractionCard 接管)。
43310
- */
43311
- function OpenUiInteraction({ channel }) {
43312
- const session = useSyncExternalStore(channel.subscribe, () => channel.current);
43313
- const [ready, setReady] = useState();
43314
- useEffect(() => {
43315
- channel.attach();
43316
- return () => channel.detach();
43317
- }, [channel]);
43318
- useEffect(() => {
43319
- let cancelled = false;
43320
- (async () => {
43321
- try {
43322
- const { openui, z } = await loadOpenUiPeers();
43323
- if (!cancelled) setReady({
43324
- Renderer: openui.Renderer,
43325
- library: buildOpenUiLibrary(openui, z)
43326
- });
43327
- } catch (e) {
43328
- if (!cancelled) channel.fail(messageOf(e));
43329
- }
43330
- })();
43331
- return () => {
43332
- cancelled = true;
43333
- };
43334
- }, [channel]);
43335
- if (!session || !ready) return null;
43336
- const { Renderer, library } = ready;
43337
- return /* @__PURE__ */ jsx("div", {
43338
- "data-testid": "openui-interaction",
43339
- className: "rounded-card border border-border bg-surface px-4 py-4 shadow-card",
43340
- children: /* @__PURE__ */ jsx(Renderer, {
43341
- response: session.lang,
43342
- library,
43343
- onAction: (action) => channel.handleAction(action)
43344
- })
43345
- });
43346
- }
43347
-
43348
42867
  //#endregion
43349
42868
  //#region src/react/OpenUiSurfaceHost.tsx
43350
- /** Renders persisted UiSurface snapshots through the OpenUI extension without a live bridge. @experimental */
43351
- function OpenUiSurfaceSnapshotHost({ snapshots, registry, onAction, drafts, onDraftChange }) {
43352
- const [ready, setReady] = useState();
42869
+ /** Renders persisted UI spec snapshots through the OpenUI extension without a live bridge. @experimental */
42870
+ function OpenUiSurfaceSnapshotHost({ snapshots, onAction }) {
42871
+ const [ready, setReady] = useState(false);
43353
42872
  const [fallbackReason, setFallbackReason] = useState();
43354
- const emitAction = useEffectEvent((event) => onAction?.(event));
43355
42873
  useEffect(() => {
43356
42874
  let cancelled = false;
43357
42875
  (async () => {
43358
42876
  try {
43359
- const { openui, z } = await loadOpenUiPeers();
43360
- if (!cancelled) setReady({
43361
- Renderer: openui.Renderer,
43362
- library: buildOpenUiLibrary(openui, z, {
43363
- root: "WebSkillSurfaceHost",
43364
- ...registry ? { registry } : {},
43365
- onSurfaceAction: emitAction,
43366
- ...drafts ? { drafts } : {},
43367
- ...onDraftChange ? { onSurfaceDraftChange: onDraftChange } : {}
43368
- })
43369
- });
42877
+ await loadOpenUiPeers();
42878
+ if (!cancelled) setReady(true);
43370
42879
  } catch (error) {
43371
42880
  const reason = error?.code === "UI_UNAVAILABLE" ? "openui-peer-missing" : "openui-load-failed";
43372
42881
  if (reason !== "openui-peer-missing") console.warn("[webskill] OpenUI renderer unavailable, falling back to native", { error });
@@ -43376,63 +42885,40 @@ function OpenUiSurfaceSnapshotHost({ snapshots, registry, onAction, drafts, onDr
43376
42885
  return () => {
43377
42886
  cancelled = true;
43378
42887
  };
43379
- }, [registry]);
42888
+ }, []);
43380
42889
  if (snapshots.length === 0) return null;
43381
- const specs = snapshots.filter((snapshot) => isUiSpecSurface(snapshot.surface));
43382
- const legacy = snapshots.filter((snapshot) => !isUiSpecSurface(snapshot.surface));
43383
- const specNodes = specs.map((snapshot) => {
43384
- const surface = snapshot.surface;
43385
- return /* @__PURE__ */ jsx(OpenUiSpecSurface, { spec: surface.props.spec }, surface.id);
43386
- });
43387
- if (!ready) return /* @__PURE__ */ jsxs("div", {
42890
+ return /* @__PURE__ */ jsx("div", {
43388
42891
  "data-testid": "openui-surface-host",
43389
- "data-renderer": "native-fallback",
42892
+ "data-renderer": ready ? "openui" : "native-fallback",
43390
42893
  ...fallbackReason ? { "data-fallback-reason": fallbackReason } : {},
43391
- children: [specNodes, legacy.length > 0 ? /* @__PURE__ */ jsx(UiSurfaceSnapshotList, {
43392
- snapshots: legacy,
43393
- ...registry ? { registry } : {},
43394
- ...onAction ? { onAction: emitAction } : {},
43395
- ...drafts ? { drafts } : {},
43396
- ...onDraftChange ? { onDraftChange } : {}
43397
- }) : null]
43398
- });
43399
- const { Renderer, library } = ready;
43400
- return /* @__PURE__ */ jsxs("div", {
43401
- "data-testid": "openui-surface-host",
43402
- "data-renderer": "openui",
43403
- children: [specNodes, legacy.map((snapshot) => /* @__PURE__ */ jsx(Renderer, {
43404
- response: toOpenUiSurfaceLang(snapshot),
43405
- library
43406
- }, snapshot.surface.id))]
42894
+ children: snapshots.map((snapshot) => /* @__PURE__ */ jsx(OpenUiSpecSurface, {
42895
+ spec: snapshot.node,
42896
+ surfaceId: snapshot.id,
42897
+ actions: snapshot.actions ?? [],
42898
+ ...onAction ? { onAction } : {}
42899
+ }, snapshot.id))
43407
42900
  });
43408
42901
  }
43409
- /** Renders serializable UiSurface descriptors through an OpenUI library extension. @experimental */
43410
- function OpenUiSurfaceHost({ bridge, runId, registry }) {
42902
+ /** Renders WebSkill UI spec snapshots through an OpenUI library extension. @experimental */
42903
+ function OpenUiSurfaceHost({ bridge, runId }) {
43411
42904
  const snapshots = useSyncExternalStore(bridge.subscribe, () => bridge.surfaceSnapshots);
43412
42905
  const visibleSnapshots = runId === void 0 ? snapshots : snapshots.filter((snapshot) => snapshot.runId === runId);
43413
42906
  const handleAction = (event) => {
43414
- const action = fromOpenUiSurfaceAction({
43415
- type: OPENUI_SURFACE_ACTION,
43416
- params: event
43417
- });
43418
- if (!action?.runId || !action.nonce) return;
42907
+ const snapshot = visibleSnapshots.find((candidate) => candidate.id === event.surfaceId);
42908
+ const capability = snapshot?.actions?.find((candidate) => candidate.id === event.actionId);
42909
+ if (!snapshot?.runId || !capability?.nonce) return;
43419
42910
  bridge.resolveSurfaceAction({
43420
- runId: action.runId,
43421
- surfaceId: action.surfaceId,
43422
- actionId: action.actionId,
43423
- intent: action.intent,
43424
- nonce: action.nonce,
43425
- ...action.value ? { value: action.value } : {},
43426
- ...action.cancelled ? { cancelled: true } : {}
42911
+ runId: snapshot.runId,
42912
+ surfaceId: event.surfaceId,
42913
+ actionId: event.actionId,
42914
+ intent: event.intent,
42915
+ nonce: capability.nonce,
42916
+ ...event.value ? { value: event.value } : {}
43427
42917
  });
43428
42918
  };
43429
- const drafts = runId ? bridge.surfaceDrafts(runId) : {};
43430
42919
  return /* @__PURE__ */ jsx(OpenUiSurfaceSnapshotHost, {
43431
42920
  snapshots: visibleSnapshots,
43432
- ...registry ? { registry } : {},
43433
- onAction: handleAction,
43434
- drafts,
43435
- onDraftChange: (event) => bridge.setSurfaceDraft(event.runId, event.surfaceId, event.value)
42921
+ onAction: handleAction
43436
42922
  });
43437
42923
  }
43438
42924
 
@@ -53510,6 +52996,7 @@ function Block({ block, runId, onDownload }) {
53510
52996
  /**
53511
52997
  * 结果块列表(AnimatePresence 逐块入场):markdown / json / table / chart / image / file。
53512
52998
  * 历史消息(ChatMessage.blocks)与当前 run(ResultBlocksPro)共用的渲染形态。
52999
+ * @stable
53513
53000
  */
53514
53001
  function ResultBlockList({ blocks, runId, onDownload }) {
53515
53002
  return /* @__PURE__ */ jsx(AnimatePresence, {
@@ -53531,6 +53018,7 @@ function ResultBlockList({ blocks, runId, onDownload }) {
53531
53018
  /**
53532
53019
  * run 完成结果块渐进挂载(AnimatePresence 逐块入场):
53533
53020
  * markdown / json / table / chart / image / file + artifact 下载卡。
53021
+ * @stable
53534
53022
  */
53535
53023
  function ResultBlocksPro({ bridge, activeRunId, onDownload }) {
53536
53024
  const t = useT();
@@ -53573,7 +53061,7 @@ function ResultBlocksPro({ bridge, activeRunId, onDownload }) {
53573
53061
 
53574
53062
  //#endregion
53575
53063
  //#region src/react/SkillBadges.tsx
53576
- /** 消息底部展示该 run 激活的技能名徽章(无技能不渲染) */
53064
+ /** 消息底部展示该 run 激活的技能名徽章(无技能不渲染) @stable */
53577
53065
  function SkillBadges({ skills }) {
53578
53066
  const t = useT();
53579
53067
  if (skills.length === 0) return null;
@@ -53599,56 +53087,42 @@ function SkillBadges({ skills }) {
53599
53087
 
53600
53088
  //#endregion
53601
53089
  //#region src/react/VercelSurfaceHost.tsx
53602
- /** Renders persisted Vercel-compatible data parts without subscribing to a live bridge. @experimental */
53603
- function VercelSurfaceSnapshotHost({ snapshots, registry, onAction, drafts, onDraftChange }) {
53604
- const dataParts = snapshots.map((snapshot) => toVercelSurfaceDataPart(snapshot));
53605
- if (dataParts.length === 0) return null;
53606
- const specs = dataParts.filter((part) => isUiSpecSurface(part.data.snapshot.surface));
53607
- const legacy = dataParts.filter((part) => !isUiSpecSurface(part.data.snapshot.surface));
53608
- return /* @__PURE__ */ jsxs("div", {
53090
+ /** Renders persisted UI spec snapshots through the json-render registry without a live bridge. @experimental */
53091
+ function VercelSurfaceSnapshotHost({ snapshots, onAction }) {
53092
+ if (snapshots.length === 0) return null;
53093
+ return /* @__PURE__ */ jsx("div", {
53609
53094
  "data-testid": "vercel-surface-snapshot-host",
53610
- "data-surface-parts": dataParts.length,
53611
- children: [specs.map((part) => {
53612
- const surface = part.data.snapshot.surface;
53613
- return /* @__PURE__ */ jsx(JsonRenderSpecSurface, { spec: surface.props.spec }, surface.id);
53614
- }), legacy.length > 0 ? /* @__PURE__ */ jsx(UiSurfaceSnapshotList, {
53615
- snapshots: legacy.map((part) => part.data.snapshot),
53616
- ...registry ? { registry } : {},
53617
- ...onAction ? { onAction } : {},
53618
- ...drafts ? { drafts } : {},
53619
- ...onDraftChange ? { onDraftChange } : {}
53620
- }) : null]
53095
+ "data-surface-parts": snapshots.length,
53096
+ children: snapshots.map((snapshot) => /* @__PURE__ */ jsx(JsonRenderSpecSurface, {
53097
+ spec: snapshot.node,
53098
+ surfaceId: snapshot.id,
53099
+ actions: snapshot.actions ?? [],
53100
+ ...onAction ? { onAction } : {}
53101
+ }, snapshot.id))
53621
53102
  });
53622
53103
  }
53623
- /** Consumes Vercel-compatible surface data parts with a controlled React surface renderer. @experimental */
53624
- function VercelSurfaceHost({ bridge, runId, registry }) {
53104
+ /** Consumes WebSkill UI spec snapshots with a json-render surface renderer. @experimental */
53105
+ function VercelSurfaceHost({ bridge, runId }) {
53625
53106
  const visibleSnapshots = useSyncExternalStore(bridge.subscribe, () => bridge.surfaceSnapshots).filter((snapshot) => runId === void 0 || snapshot.runId === runId);
53107
+ if (visibleSnapshots.length === 0) return null;
53626
53108
  const handleAction = (event) => {
53627
- const action = fromVercelSurfaceAction({
53628
- type: VERCEL_SURFACE_ACTION_DATA_PART_TYPE,
53629
- data: event
53630
- });
53631
- if (!action?.runId || !action.nonce) return;
53109
+ const snapshot = visibleSnapshots.find((candidate) => candidate.id === event.surfaceId);
53110
+ const capability = snapshot?.actions?.find((candidate) => candidate.id === event.actionId);
53111
+ if (!snapshot?.runId || !capability?.nonce) return;
53632
53112
  bridge.resolveSurfaceAction({
53633
- runId: action.runId,
53634
- surfaceId: action.surfaceId,
53635
- actionId: action.actionId,
53636
- intent: action.intent,
53637
- nonce: action.nonce,
53638
- ...action.value ? { value: action.value } : {},
53639
- ...action.cancelled ? { cancelled: true } : {}
53113
+ runId: snapshot.runId,
53114
+ surfaceId: event.surfaceId,
53115
+ actionId: event.actionId,
53116
+ intent: event.intent,
53117
+ nonce: capability.nonce,
53118
+ ...event.value ? { value: event.value } : {}
53640
53119
  });
53641
53120
  };
53642
- if (visibleSnapshots.length === 0) return null;
53643
- const drafts = runId ? bridge.surfaceDrafts(runId) : {};
53644
53121
  return /* @__PURE__ */ jsx("div", {
53645
53122
  "data-testid": "vercel-surface-host",
53646
53123
  children: /* @__PURE__ */ jsx(VercelSurfaceSnapshotHost, {
53647
53124
  snapshots: visibleSnapshots,
53648
- ...registry ? { registry } : {},
53649
- onAction: handleAction,
53650
- drafts,
53651
- onDraftChange: (event) => bridge.setSurfaceDraft(event.runId, event.surfaceId, event.value)
53125
+ onAction: handleAction
53652
53126
  })
53653
53127
  });
53654
53128
  }
@@ -53660,23 +53134,30 @@ function HistoricalSurfaces({ snapshots, renderer, registry }) {
53660
53134
  snapshots,
53661
53135
  ...registry ? { registry } : {}
53662
53136
  });
53663
- if (renderer === "openui") return /* @__PURE__ */ jsx(OpenUiSurfaceSnapshotHost, {
53664
- snapshots,
53665
- ...registry ? { registry } : {}
53666
- });
53667
- if (renderer === "vercel") return /* @__PURE__ */ jsx(VercelSurfaceSnapshotHost, {
53668
- snapshots,
53669
- ...registry ? { registry } : {}
53670
- });
53137
+ if (renderer === "openui") return /* @__PURE__ */ jsx(OpenUiSurfaceSnapshotHost, { snapshots });
53138
+ if (renderer === "vercel") return /* @__PURE__ */ jsx(VercelSurfaceSnapshotHost, { snapshots });
53671
53139
  return /* @__PURE__ */ jsx(UiSurfaceSnapshotList, {
53672
53140
  snapshots,
53673
53141
  ...registry ? { registry } : {}
53674
53142
  });
53675
53143
  }
53144
+ /**
53145
+ * 旧版本会话里的 surface 条目是六类 `{ surface: { kind } }`,没有节点树,任何渲染器都吃不下。
53146
+ * 判定只看渲染入口是否成立,不做深校验:深校验属于产出侧,历史消息只决定「渲染还是占位」。
53147
+ */
53148
+ function isRenderableSpec(snapshot) {
53149
+ if (typeof snapshot !== "object" || snapshot === null) return false;
53150
+ const node = snapshot.node;
53151
+ if (typeof node !== "object" || node === null) return false;
53152
+ return typeof node.component === "string";
53153
+ }
53676
53154
  /** WebSkill domain content rendered beneath a Base message without choosing its chat layout. */
53677
53155
  function WebSkillMessageExtensions({ message, onDownload, surfaceRegistry, renderer }) {
53156
+ const t = useT();
53678
53157
  if (message.role === "user") return null;
53679
53158
  const blocks = message.blocks?.filter((block) => block.type !== "markdown" || block.text.trim() !== message.text.trim());
53159
+ const surfaces = message.surfaces ?? [];
53160
+ const renderable = surfaces.filter(isRenderableSpec);
53680
53161
  return /* @__PURE__ */ jsxs("div", {
53681
53162
  className: "min-w-0 w-full",
53682
53163
  children: [
@@ -53685,11 +53166,16 @@ function WebSkillMessageExtensions({ message, onDownload, surfaceRegistry, rende
53685
53166
  runId: message.runId ?? "",
53686
53167
  ...onDownload ? { onDownload } : {}
53687
53168
  }) : null,
53688
- message.surfaces && message.surfaces.length > 0 ? /* @__PURE__ */ jsx(HistoricalSurfaces, {
53689
- snapshots: message.surfaces,
53169
+ renderable.length > 0 ? /* @__PURE__ */ jsx(HistoricalSurfaces, {
53170
+ snapshots: renderable,
53690
53171
  ...renderer ? { renderer } : {},
53691
53172
  ...surfaceRegistry ? { registry: surfaceRegistry } : {}
53692
53173
  }) : null,
53174
+ renderable.length < surfaces.length ? /* @__PURE__ */ jsx("p", {
53175
+ "data-testid": "legacy-surface-placeholder",
53176
+ className: "my-2 rounded-lg border border-border bg-canvas px-3 py-2 text-xs text-muted",
53177
+ children: t("surface.legacy.placeholder")
53178
+ }) : null,
53693
53179
  /* @__PURE__ */ jsx(SkillBadges, { skills: message.activeSkillNames ?? [] })
53694
53180
  ]
53695
53181
  });
@@ -53906,23 +53392,35 @@ const start = (runId, at) => ({
53906
53392
  text: "",
53907
53393
  chunks: 0
53908
53394
  });
53909
- function appendPhase(live, phase, at, data) {
53910
- const display = DISPLAY_PHASE[phase];
53395
+ function appendPhase(live, event) {
53396
+ const display = DISPLAY_PHASE[event.phase];
53911
53397
  const last = live.phases.at(-1);
53912
53398
  if (last?.phase === display) return live;
53913
53399
  const closed = last ? [...live.phases.slice(0, -1), {
53914
53400
  ...last,
53915
- endedAt: at
53401
+ endedAt: event.at
53916
53402
  }] : [];
53403
+ const note = phaseNote(event);
53917
53404
  return {
53918
53405
  ...live,
53919
53406
  phases: [...closed, {
53920
53407
  phase: display,
53921
- at,
53922
- ...data ? { data } : {}
53408
+ at: event.at,
53409
+ ...note ? { note } : {}
53923
53410
  }]
53924
53411
  };
53925
53412
  }
53413
+ /** 相位摘要:唯一读 `data` 的地方,此处事件还带着 `phase` 判别,下游不必再猜键名 */
53414
+ function phaseNote(event) {
53415
+ switch (event.phase) {
53416
+ case "route": return event.data.strategy;
53417
+ case "activate": return event.data.skillName;
53418
+ case "interact": return event.data.kind === "interaction" ? event.data.interactionType : event.data.actionId;
53419
+ case "complete":
53420
+ case "fail": return event.data.reason;
53421
+ default: return;
53422
+ }
53423
+ }
53926
53424
  /**
53927
53425
  * ChatEvent → 进行中 run 的 part 累积器。返回 undefined 表示当前没有进行中的 run。
53928
53426
  * 只处理属于当前 runId 的事件,跨 run 的迟到事件被忽略。
@@ -53936,7 +53434,7 @@ function reduceLiveRun(live, event, at) {
53936
53434
  }
53937
53435
  if (!live || !("runId" in event) || event.runId !== live.runId) return live;
53938
53436
  switch (event.type) {
53939
- case "lifecycle": return appendPhase(live, event.phase, event.at, event.data);
53437
+ case "lifecycle": return appendPhase(live, event);
53940
53438
  case "skill-activated": return {
53941
53439
  ...live,
53942
53440
  skills: event.skills
@@ -53975,7 +53473,7 @@ function reduceLiveRun(live, event, at) {
53975
53473
 
53976
53474
  //#endregion
53977
53475
  //#region src/react/ErrorCard.tsx
53978
- /** 结构化错误卡:错误码徽章 + 描述 + 建议(engine error 事件 / 失败 run) */
53476
+ /** 结构化错误卡:错误码徽章 + 描述 + 建议(engine error 事件 / 失败 run) @stable */
53979
53477
  function ErrorCard({ code, message, onDismiss }) {
53980
53478
  const t = useT();
53981
53479
  return /* @__PURE__ */ jsx("div", {
@@ -54029,9 +53527,42 @@ function ErrorCard({ code, message, onDismiss }) {
54029
53527
  });
54030
53528
  }
54031
53529
 
53530
+ //#endregion
53531
+ //#region src/react/interactionLabels.ts
53532
+ /**
53533
+ * 交互卡的界面文案:生成器(`@webskill/ui`)与语言无关,本地化由持有字典的宿主注入。
53534
+ * 四档共用本函数,切换渲染档不会让同一个请求出现两套文案。
53535
+ */
53536
+ function interactionLabels(request, t) {
53537
+ return {
53538
+ title: request.type === "form" && request.title ? request.title : t(`interaction.${request.type}.title`),
53539
+ submitLabel: t(request.type === "authorize" ? "interaction.allow" : request.type === "confirm" ? "interaction.confirm" : "interaction.submit"),
53540
+ cancelLabel: t(request.type === "authorize" ? "interaction.deny" : "interaction.cancel")
53541
+ };
53542
+ }
53543
+
54032
53544
  //#endregion
54033
53545
  //#region src/react/InteractionCard.tsx
54034
- /** 订阅 bridge.pending 渲染五类交互卡(ask/confirm/form/select/authorize,native) */
53546
+ /** 交互表单的两个动作:与 surface 侧共用 catalog 的 submit / cancel 语义 */
53547
+ const INTERACTION_ACTIONS$1 = [{
53548
+ id: "submit",
53549
+ intent: "submit"
53550
+ }, {
53551
+ id: "cancel",
53552
+ intent: "cancel"
53553
+ }];
53554
+ const kindIcons = {
53555
+ ask: MessageCircleQuestionMark,
53556
+ confirm: CircleQuestionMark,
53557
+ form: ClipboardList,
53558
+ select: ListChecks,
53559
+ authorize: ShieldAlert
53560
+ };
53561
+ /**
53562
+ * 订阅 bridge.pending 渲染五类交互卡(native 档;其它档降级后也由它接管)。
53563
+ * 表单本体走 `NativeSpecSurface`——与 native 档 surface 侧同一个渲染入口。
53564
+ * @stable
53565
+ */
54035
53566
  function InteractionCard({ bridge }) {
54036
53567
  const pending = useSyncExternalStore(bridge.subscribe, () => bridge.pending);
54037
53568
  if (!pending) return null;
@@ -54040,102 +53571,25 @@ function InteractionCard({ bridge }) {
54040
53571
  bridge
54041
53572
  }, pending.id);
54042
53573
  }
54043
- /** 单个表单控件(ask/form 用;data-webskill-control 契约与 collectValues 对齐) */
54044
- function Field({ control, invalid }) {
54045
- const t = useT();
54046
- const id = `webskill-field-${control.name}`;
54047
- const label = /* @__PURE__ */ jsxs(Fragment$1, { children: [control.label, control.required ? /* @__PURE__ */ jsx("span", {
54048
- className: "text-danger",
54049
- children: " *"
54050
- }) : null] });
54051
- let element;
54052
- switch (control.control) {
54053
- case "boolean":
54054
- element = /* @__PURE__ */ jsx(Checkbox, {
54055
- id,
54056
- "data-webskill-control": control.name,
54057
- defaultChecked: control.defaultValue === true ? true : void 0,
54058
- invalid
54059
- });
54060
- break;
54061
- case "select":
54062
- element = /* @__PURE__ */ jsx("select", {
54063
- id,
54064
- "data-webskill-control": control.name,
54065
- "aria-invalid": invalid || void 0,
54066
- className: "h-9 w-full rounded-md border border-input bg-surface px-3 text-sm text-ink shadow-xs outline-none transition-colors focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/20",
54067
- ...control.defaultValue !== void 0 ? { defaultValue: JSON.stringify(control.defaultValue) } : {},
54068
- children: (control.options ?? []).map((option, i) => /* @__PURE__ */ jsx("option", {
54069
- value: JSON.stringify(option.value),
54070
- children: option.label
54071
- }, i))
54072
- });
54073
- break;
54074
- case "textarea":
54075
- element = /* @__PURE__ */ jsx(Textarea, {
54076
- id,
54077
- "data-webskill-control": control.name,
54078
- invalid,
54079
- rows: 3,
54080
- defaultValue: control.defaultValue !== void 0 ? String(control.defaultValue) : ""
54081
- });
54082
- break;
54083
- default: element = /* @__PURE__ */ jsx(Input, {
54084
- type: control.control === "number" ? "number" : "text",
54085
- id,
54086
- "data-webskill-control": control.name,
54087
- invalid,
54088
- defaultValue: control.defaultValue !== void 0 ? String(control.defaultValue) : ""
54089
- });
54090
- }
54091
- return /* @__PURE__ */ jsx(FormField, {
54092
- label,
54093
- htmlFor: id,
54094
- ...control.description ? { description: control.description } : {},
54095
- ...invalid ? { error: t("interaction.required") } : {},
54096
- children: element
54097
- });
54098
- }
54099
- const kindIcons = {
54100
- ask: MessageCircleQuestionMark,
54101
- confirm: CircleQuestionMark,
54102
- form: ClipboardList,
54103
- select: ListChecks,
54104
- authorize: ShieldAlert
54105
- };
54106
53574
  function ActiveInteractionCard({ request, bridge }) {
54107
53575
  const t = useT();
54108
- const model = interactionToFormModel(request);
54109
- const formRef = useRef(null);
54110
- const [invalid, setInvalid] = useState([]);
54111
- const resolve = (value) => bridge.resolve({
54112
- id: request.id,
54113
- value
54114
- });
54115
- const cancel = () => bridge.resolve({
54116
- id: request.id,
54117
- cancelled: true
54118
- });
54119
- const submitForm = () => {
54120
- if (!formRef.current) return;
54121
- const { values, missingRequired } = collectValues(model.controls, formRef.current);
54122
- if (missingRequired.length > 0) {
54123
- setInvalid(missingRequired);
53576
+ const spec = interactionToUiSpec(request, interactionLabels(request, t));
53577
+ const isAuthorize = request.type === "authorize";
53578
+ const Icon = kindIcons[request.type];
53579
+ const handleAction = (event) => {
53580
+ if (event.intent !== "submit") {
53581
+ bridge.resolve({
53582
+ id: request.id,
53583
+ cancelled: true
53584
+ });
54124
53585
  return;
54125
53586
  }
54126
- resolve(shapeInteractionValue(model, values));
54127
- };
54128
- const isAuthorize = model.kind === "authorize";
54129
- const Icon = kindIcons[model.kind];
54130
- const title = resolveTitle(model, t);
54131
- const body = renderBody(model, request, {
54132
- t,
54133
- formRef,
54134
- invalid,
54135
- submitForm,
54136
- resolve,
54137
- cancel
54138
- });
53587
+ const value = shapeInteractionValue(interactionToFormModel(request), event.value ?? {});
53588
+ bridge.resolve({
53589
+ id: request.id,
53590
+ value
53591
+ });
53592
+ };
54139
53593
  return /* @__PURE__ */ jsxs(motion.div, {
54140
53594
  ...fadeInUp,
54141
53595
  className: `rounded-card border shadow-card ${isAuthorize ? "border-warning/50 bg-warning-soft" : "border-border bg-surface"}`,
@@ -54147,132 +53601,51 @@ function ActiveInteractionCard({ request, bridge }) {
54147
53601
  className: "size-4",
54148
53602
  "aria-hidden": true
54149
53603
  })
54150
- }), /* @__PURE__ */ jsxs("div", {
54151
- className: "min-w-0 flex-1",
54152
- children: [/* @__PURE__ */ jsx("h3", {
54153
- className: "font-display text-sm font-semibold text-ink",
54154
- children: title
54155
- }), model.message ? /* @__PURE__ */ jsx("p", {
54156
- className: "mt-0.5 text-sm break-words text-muted",
54157
- children: model.message
54158
- }) : null]
54159
- })]
53604
+ }), request.type === "authorize" ? /* @__PURE__ */ jsxs("div", {
53605
+ className: "flex min-w-0 flex-1 flex-col gap-2",
53606
+ children: [
53607
+ /* @__PURE__ */ jsxs("div", {
53608
+ className: "flex items-center gap-2",
53609
+ children: [/* @__PURE__ */ jsx("span", {
53610
+ className: "text-xs font-medium text-muted",
53611
+ children: t("interaction.authorize.capability")
53612
+ }), /* @__PURE__ */ jsx(Badge, {
53613
+ tone: "warning",
53614
+ children: request.capability
53615
+ })]
53616
+ }),
53617
+ /* @__PURE__ */ jsx("p", {
53618
+ className: "text-xs text-muted",
53619
+ children: t("interaction.authorize.description")
53620
+ }),
53621
+ request.details !== void 0 ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
53622
+ className: "mb-1 text-xs font-medium text-muted",
53623
+ children: t("interaction.authorize.details")
53624
+ }), /* @__PURE__ */ jsx("pre", {
53625
+ className: "max-h-40 overflow-auto rounded-lg border border-border bg-canvas p-3 font-mono text-xs text-ink",
53626
+ children: JSON.stringify(request.details, null, 2)
53627
+ })] }) : null
53628
+ ]
53629
+ }) : null]
54160
53630
  }), /* @__PURE__ */ jsx("div", {
54161
53631
  className: "px-4 py-4",
54162
- children: body
54163
- })]
54164
- });
54165
- }
54166
- function resolveTitle(model, t) {
54167
- if (model.title) return model.title;
54168
- switch (model.kind) {
54169
- case "ask": return t("interaction.ask.title");
54170
- case "confirm": return t("interaction.confirm.title");
54171
- case "select": return t("interaction.select.title");
54172
- case "authorize": return t("interaction.authorize.title");
54173
- default: return t("interaction.form.title");
54174
- }
54175
- }
54176
- function renderBody(model, request, deps) {
54177
- const { t, formRef, invalid, submitForm, resolve, cancel } = deps;
54178
- if (model.kind === "authorize") {
54179
- const capability = request.type === "authorize" ? request.capability : void 0;
54180
- const details = request.type === "authorize" ? request.details : void 0;
54181
- return /* @__PURE__ */ jsxs("div", {
54182
- className: "flex flex-col gap-3",
54183
- children: [
54184
- capability ? /* @__PURE__ */ jsxs("div", {
54185
- className: "flex items-center gap-2",
54186
- children: [/* @__PURE__ */ jsx("span", {
54187
- className: "text-xs font-medium text-muted",
54188
- children: t("interaction.authorize.capability")
54189
- }), /* @__PURE__ */ jsx(Badge, {
54190
- tone: "warning",
54191
- children: capability
54192
- })]
54193
- }) : null,
54194
- /* @__PURE__ */ jsx("p", {
54195
- className: "text-xs text-muted",
54196
- children: t("interaction.authorize.description")
54197
- }),
54198
- details !== void 0 ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
54199
- className: "mb-1 text-xs font-medium text-muted",
54200
- children: t("interaction.authorize.details")
54201
- }), /* @__PURE__ */ jsx("pre", {
54202
- className: "max-h-40 overflow-auto rounded-lg border border-border bg-canvas p-3 font-mono text-xs text-ink",
54203
- children: JSON.stringify(details, null, 2)
54204
- })] }) : null,
54205
- /* @__PURE__ */ jsxs("div", {
54206
- className: "flex items-center gap-2",
54207
- children: [/* @__PURE__ */ jsx(Button, {
54208
- onClick: () => resolve(shapeInteractionValue(model, {})),
54209
- children: t("interaction.allow")
54210
- }), /* @__PURE__ */ jsx(Button, {
54211
- variant: "danger",
54212
- onClick: cancel,
54213
- children: t("interaction.deny")
54214
- })]
54215
- })
54216
- ]
54217
- });
54218
- }
54219
- if (model.kind === "confirm") return /* @__PURE__ */ jsxs("div", {
54220
- className: "flex items-center gap-2",
54221
- children: [/* @__PURE__ */ jsx(Button, {
54222
- onClick: () => resolve(shapeInteractionValue(model, { confirmed: true })),
54223
- children: t("interaction.confirm")
54224
- }), /* @__PURE__ */ jsx(Button, {
54225
- variant: "secondary",
54226
- onClick: cancel,
54227
- children: t("interaction.cancel")
54228
- })]
54229
- });
54230
- if (model.kind === "select") return /* @__PURE__ */ jsxs("div", {
54231
- className: "flex flex-col gap-3",
54232
- children: [/* @__PURE__ */ jsx("ul", {
54233
- className: "flex flex-col gap-1.5",
54234
- children: (model.controls[0]?.options ?? []).map((option, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
54235
- type: "button",
54236
- onClick: () => resolve(shapeInteractionValue(model, { selected: option.value })),
54237
- className: "w-full cursor-pointer rounded-lg border border-border bg-surface px-3.5 py-2.5 text-left text-sm text-ink transition-colors hover:border-ring hover:bg-subtle",
54238
- children: option.label
54239
- }) }, i))
54240
- }), /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, {
54241
- variant: "secondary",
54242
- onClick: cancel,
54243
- children: t("interaction.cancel")
54244
- }) })]
54245
- });
54246
- return /* @__PURE__ */ jsxs("form", {
54247
- ref: formRef,
54248
- noValidate: true,
54249
- className: "flex flex-col gap-3",
54250
- onSubmit: (event) => {
54251
- event.preventDefault();
54252
- submitForm();
54253
- },
54254
- children: [model.controls.map((control) => /* @__PURE__ */ jsx(Field, {
54255
- control,
54256
- invalid: invalid.includes(control.name)
54257
- }, control.name)), /* @__PURE__ */ jsxs("div", {
54258
- className: "flex items-center gap-2",
54259
- children: [/* @__PURE__ */ jsx(Button, {
54260
- type: "submit",
54261
- children: t("interaction.submit")
54262
- }), /* @__PURE__ */ jsx(Button, {
54263
- variant: "secondary",
54264
- onClick: cancel,
54265
- children: t("interaction.cancel")
54266
- })]
53632
+ children: /* @__PURE__ */ jsx(NativeSpecSurface, {
53633
+ surfaceId: request.id,
53634
+ spec,
53635
+ actions: INTERACTION_ACTIONS$1,
53636
+ onAction: handleAction
53637
+ })
54267
53638
  })]
54268
53639
  });
54269
53640
  }
54270
53641
 
54271
53642
  //#endregion
54272
53643
  //#region src/react/InterruptedBanner.tsx
53644
+ const resumable = (list) => list.filter((entry) => !isUnsupportedRunSnapshot(entry));
54273
53645
  /**
54274
53646
  * 未完成对话提示条:挂载/会话切换时检查 interrupted 快照,非空则顶部 banner
54275
53647
  * + Resume(engine.resume 后等待中的表单重新进入 bridge.pending)。
53648
+ * @stable
54276
53649
  */
54277
53650
  function InterruptedBanner({ engine, sessionId }) {
54278
53651
  const t = useT();
@@ -54280,7 +53653,7 @@ function InterruptedBanner({ engine, sessionId }) {
54280
53653
  useEffect(() => {
54281
53654
  let cancelled = false;
54282
53655
  engine.listInterrupted().then((list) => {
54283
- if (!cancelled) setInterrupted(list);
53656
+ if (!cancelled) setInterrupted(resumable(list));
54284
53657
  }).catch(() => {});
54285
53658
  return () => {
54286
53659
  cancelled = true;
@@ -54290,7 +53663,7 @@ function InterruptedBanner({ engine, sessionId }) {
54290
53663
  if (!current) return null;
54291
53664
  const resume = () => {
54292
53665
  engine.resume(current.runId).then(async () => {
54293
- setInterrupted(await engine.listInterrupted());
53666
+ setInterrupted(resumable(await engine.listInterrupted()));
54294
53667
  });
54295
53668
  };
54296
53669
  return /* @__PURE__ */ jsxs("div", {
@@ -54324,6 +53697,68 @@ function InterruptedBanner({ engine, sessionId }) {
54324
53697
  });
54325
53698
  }
54326
53699
 
53700
+ //#endregion
53701
+ //#region src/react/SpecInteraction.tsx
53702
+ /** 交互表单的两个动作:与 surface 侧共用 catalog 的 submit / cancel 语义 */
53703
+ const INTERACTION_ACTIONS = [{
53704
+ id: "submit",
53705
+ intent: "submit"
53706
+ }, {
53707
+ id: "cancel",
53708
+ intent: "cancel"
53709
+ }];
53710
+ /** 各档的 spec 渲染入口——交互(路径 A)与 surface(路径 B)在同一档下命中同一个组件 */
53711
+ const SURFACES = {
53712
+ a2ui: A2uiSpecSurface,
53713
+ openui: OpenUiSpecSurface,
53714
+ vercel: JsonRenderSpecSurface
53715
+ };
53716
+ /** optional peer 探测:缺包时通知通道,桥据此粘性降级 native(不悬挂请求、不阻断 run) */
53717
+ const PROBES = {
53718
+ a2ui: probeA2uiAvailability,
53719
+ openui: probeOpenUiAvailability,
53720
+ vercel: probeJsonRenderAvailability
53721
+ };
53722
+ const MISSING_PEER = {
53723
+ a2ui: "@a2ui/lit and @a2ui/web_core are not installed in this host",
53724
+ openui: "@openuidev/react-lang is not installed in this host",
53725
+ vercel: "@json-render/core and @json-render/react are not installed in this host"
53726
+ };
53727
+ /**
53728
+ * 非 native 三档的交互视图(路径 A):订阅 SpecInteractionChannel(core 产出 catalog
53729
+ * 节点树),交给本档 surface 侧同一个渲染入口渲染;提交/取消经 handleAction 回传。
53730
+ * 未挂载时桥直接降级 native(不悬挂请求)。
53731
+ * @experimental
53732
+ */
53733
+ function SpecInteraction({ channel, renderer }) {
53734
+ const t = useT();
53735
+ const session = useSyncExternalStore(channel.subscribe, () => channel.current);
53736
+ useEffect(() => {
53737
+ channel.labelsFor = (request) => interactionLabels(request, t);
53738
+ }, [channel, t]);
53739
+ useEffect(() => {
53740
+ channel.attach();
53741
+ return () => channel.detach();
53742
+ }, [channel]);
53743
+ useEffect(() => {
53744
+ let cancelled = false;
53745
+ PROBES[renderer]().then((available) => {
53746
+ if (!cancelled && !available) channel.fail(MISSING_PEER[renderer]);
53747
+ });
53748
+ return () => {
53749
+ cancelled = true;
53750
+ };
53751
+ }, [channel, renderer]);
53752
+ if (!session) return null;
53753
+ const Surface = SURFACES[renderer];
53754
+ return /* @__PURE__ */ jsx(Surface, {
53755
+ surfaceId: session.request.id,
53756
+ spec: session.spec,
53757
+ actions: INTERACTION_ACTIONS,
53758
+ onAction: (event) => channel.handleAction(event)
53759
+ });
53760
+ }
53761
+
54327
53762
  //#endregion
54328
53763
  //#region src/react/SettingsDrawer.tsx
54329
53764
  const PROVIDERS = [
@@ -54347,7 +53782,7 @@ function createProbeClient(provider, form) {
54347
53782
  model: form.model
54348
53783
  });
54349
53784
  }
54350
- /** 设置抽屉:LLM 配置(三协议供应商 / 能力探测 / Demo model 标识 / 流式与 temperature)+ 外观(主题/语言) */
53785
+ /** 设置抽屉:LLM 配置(三协议供应商 / 能力探测 / Demo model 标识 / 流式与 temperature)+ 外观(主题/语言) @stable */
54351
53786
  function SettingsDrawer({ open, llmConfig, runtimeConfig, theme, locale, renderer, onAppearanceChange, onSaved, onClose }) {
54352
53787
  const t = useT();
54353
53788
  const [provider, setProvider] = useState("openai-compatible");
@@ -54717,53 +54152,6 @@ function useChatLayout(layout = "auto") {
54717
54152
  };
54718
54153
  }
54719
54154
 
54720
- //#endregion
54721
- //#region src/react/VercelPayloadPreview.tsx
54722
- /**
54723
- * vercel 档(payload 预览):不渲染表单,展示 InteractionRequest 经 toVercelToolInvocation
54724
- * 转换后的 Vercel AI SDK 数据契约 JSON 卡。仅提供 Cancel 结束交互(非完整运行时)。
54725
- */
54726
- function VercelPayloadPreview({ bridge }) {
54727
- const t = useT();
54728
- const pending = useSyncExternalStore(bridge.subscribe, () => bridge.pending);
54729
- if (!pending) return null;
54730
- const payload = toVercelToolInvocation(pending);
54731
- return /* @__PURE__ */ jsxs("div", {
54732
- "data-testid": "vercel-payload-preview",
54733
- className: "rounded-card border border-border bg-surface shadow-card",
54734
- children: [/* @__PURE__ */ jsxs("div", {
54735
- className: "flex items-center gap-2 px-4 pt-4",
54736
- children: [/* @__PURE__ */ jsx("h3", {
54737
- className: "font-display text-sm font-semibold text-ink",
54738
- children: t("vercel.preview.title")
54739
- }), /* @__PURE__ */ jsx(Badge, {
54740
- tone: "neutral",
54741
- children: t("vercel.preview.badge")
54742
- })]
54743
- }), /* @__PURE__ */ jsxs("div", {
54744
- className: "flex flex-col gap-3 px-4 py-4",
54745
- children: [
54746
- /* @__PURE__ */ jsx("p", {
54747
- className: "text-xs text-muted",
54748
- children: t("vercel.preview.description")
54749
- }),
54750
- /* @__PURE__ */ jsx("pre", {
54751
- className: "max-h-64 overflow-auto rounded-lg border border-border bg-canvas p-3 font-mono text-xs text-ink",
54752
- children: JSON.stringify(payload, null, 2)
54753
- }),
54754
- /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, {
54755
- variant: "secondary",
54756
- onClick: () => bridge.resolve({
54757
- id: pending.id,
54758
- cancelled: true
54759
- }),
54760
- children: t("interaction.cancel")
54761
- }) })
54762
- ]
54763
- })]
54764
- });
54765
- }
54766
-
54767
54155
  //#endregion
54768
54156
  //#region src/react/Chatbot.tsx
54769
54157
  const toErrorMessage = (e) => e instanceof Error ? e.message : String(e);
@@ -54786,14 +54174,17 @@ async function downloadArtifactFile(storage, chatRoot, file) {
54786
54174
  /**
54787
54175
  * ChatEngine owns message/session/runtime state while assistant-ui Base owns the
54788
54176
  * presentation shell and accessible thread primitives.
54177
+ * @stable
54789
54178
  */
54790
- function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, surfaceRegistry, layout = "auto", onAppearanceChange }) {
54179
+ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, surfaceRegistry, layout = "auto", onAppearanceChange, onEngineReady }) {
54791
54180
  const engine = useMemo(() => new ChatEngine(adapter, {
54792
54181
  ...config?.chatRoot ? { chatRoot: config.chatRoot } : {},
54793
54182
  ...config?.llm ? { llm: config.llm } : {},
54794
54183
  ...config?.loopConfig ? { loopConfig: config.loopConfig } : {},
54795
54184
  ...config?.runtimeConfig ? { runtimeConfig: config.runtimeConfig } : {},
54796
54185
  ...config?.generativeUi ? { generativeUi: true } : {},
54186
+ ...config?.hooks ? { hooks: config.hooks } : {},
54187
+ ...config?.governance ? { governance: config.governance } : {},
54797
54188
  ...rendererProp ? { renderer: rendererProp } : {}
54798
54189
  }), [
54799
54190
  adapter,
@@ -54802,8 +54193,13 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
54802
54193
  config?.loopConfig,
54803
54194
  config?.runtimeConfig,
54804
54195
  config?.generativeUi,
54196
+ config?.hooks,
54197
+ config?.governance,
54805
54198
  rendererProp
54806
54199
  ]);
54200
+ useEffect(() => {
54201
+ onEngineReady?.(engine);
54202
+ }, [engine, onEngineReady]);
54807
54203
  const [theme, setTheme] = useState(themeProp ?? "light");
54808
54204
  const [locale, setLocale] = useState(localeProp ?? "en");
54809
54205
  const [renderer, setRenderer] = useState(rendererProp ?? "native");
@@ -54823,6 +54219,11 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
54823
54219
  const [sessions, setSessions] = useState([]);
54824
54220
  /** 首次 listSessions 未完成(threads 客户端的 isLoading) */
54825
54221
  const [sessionsLoading, setSessionsLoading] = useState(true);
54222
+ /** 分页游标:非空即「还有更早的一页」。页大小归 SessionStore,这里不覆写 limit */
54223
+ const [sessionsCursor, setSessionsCursor] = useState();
54224
+ const [messagesCursor, setMessagesCursor] = useState();
54225
+ const [loadingEarlierSessions, setLoadingEarlierSessions] = useState(false);
54226
+ const [loadingEarlierMessages, setLoadingEarlierMessages] = useState(false);
54826
54227
  const [currentSessionId, setCurrentSessionId] = useState();
54827
54228
  const [sending, setSending] = useState(false);
54828
54229
  /** 当前活跃 runId(run-started 捕获;Stop 按钮 engine.cancel 用) */
@@ -54832,14 +54233,15 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
54832
54233
  const [settingsOpen, setSettingsOpen] = useState(false);
54833
54234
  /** 当前进行中 run 的 part 累积器(流式正文 / 工具 / 生命周期步进的唯一来源) */
54834
54235
  const [live, setLive] = useState();
54835
- /** a2ui 档交互容器显隐(interaction-requested/resolved 事件驱动) */
54836
- const [a2uiActive, setA2uiActive] = useState(false);
54837
54236
  const [interactionPending, setInteractionPending] = useState(false);
54838
54237
  /** pending 交互归属的会话(跨会话查看时隐藏交互卡,防串台) */
54839
54238
  const [interactionSessionId, setInteractionSessionId] = useState();
54840
54239
  /** currentSessionId 的 ref 镜像(事件订阅闭包 [engine] 内读取最新值) */
54841
54240
  const sessionRef = useRef(currentSessionId);
54842
54241
  sessionRef.current = currentSessionId;
54242
+ /** sessions 的 ref 镜像(handleSend 判定「这次是否会生成标题」,不想为此进依赖表) */
54243
+ const sessionsRef = useRef(sessions);
54244
+ sessionsRef.current = sessions;
54843
54245
  const { containerRef, resolved: resolvedLayout } = useChatLayout(layout);
54844
54246
  /** 附件按钮只在存储确实可写时出现(R11:不渲染点了就报错的按钮) */
54845
54247
  const [attachmentsReady, setAttachmentsReady] = useState(false);
@@ -54858,30 +54260,26 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
54858
54260
  cancelled = true;
54859
54261
  };
54860
54262
  }, [adapter, engine]);
54861
- /** a2ui 交互容器 ref:挂载即注入引擎(卸载传 null),引擎懒建 LitRendererBridge 渲染其中;
54862
- 同时为容器配置 @a2ui/markdown-it(optional peer,缺失时保持纯文本降级) */
54863
- const interactionMountRef = useCallback((el) => {
54864
- engine.setInteractionMount(el);
54865
- if (el) configureA2uiMarkdown(el).then((configured) => {
54866
- if (!configured) console.warn("A2UI markdown renderer is not configured: install @a2ui/markdown-it to render markdown in A2UI interactions");
54867
- });
54868
- }, [engine]);
54869
54263
  const refreshSessions = useCallback(async () => {
54870
- setSessions(await engine.listSessions());
54264
+ const page = await engine.listSessions();
54265
+ setSessions(page.items);
54266
+ setSessionsCursor(page.nextCursor);
54871
54267
  }, [engine]);
54872
54268
  useEffect(() => {
54873
54269
  let cancelled = false;
54874
54270
  (async () => {
54875
- const list = await engine.listSessions();
54271
+ const page = await engine.listSessions();
54876
54272
  if (cancelled) return;
54877
- setSessions(list);
54273
+ setSessions(page.items);
54274
+ setSessionsCursor(page.nextCursor);
54878
54275
  setSessionsLoading(false);
54879
- const latest = list[list.length - 1];
54276
+ const latest = page.items[page.items.length - 1];
54880
54277
  if (!latest) return;
54881
54278
  const history = await engine.selectSession(latest.id);
54882
54279
  if (cancelled) return;
54883
54280
  setCurrentSessionId(latest.id);
54884
- setMessages(history);
54281
+ setMessages(history.items);
54282
+ setMessagesCursor(history.nextCursor);
54885
54283
  })().catch((e) => {
54886
54284
  if (!cancelled) {
54887
54285
  setSessionsLoading(false);
@@ -54904,12 +54302,10 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
54904
54302
  setMessages((prev) => prev.some((message) => message.id === event.message.id) ? prev : [...prev, event.message]);
54905
54303
  break;
54906
54304
  case "interaction-requested":
54907
- setA2uiActive(true);
54908
54305
  setInteractionPending(true);
54909
54306
  setInteractionSessionId(sessionRef.current);
54910
54307
  break;
54911
54308
  case "interaction-resolved":
54912
- setA2uiActive(false);
54913
54309
  setInteractionPending(false);
54914
54310
  setInteractionSessionId(void 0);
54915
54311
  break;
@@ -54934,12 +54330,16 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
54934
54330
  }), [engine]);
54935
54331
  const handleSend = useCallback((text, attachments = []) => {
54936
54332
  (async () => {
54937
- if (!currentSessionId) {
54333
+ let sessionId = currentSessionId;
54334
+ if (!sessionId) {
54938
54335
  const meta = await engine.createSession();
54336
+ sessionId = meta.id;
54939
54337
  setCurrentSessionId(meta.id);
54940
54338
  await refreshSessions();
54941
54339
  }
54340
+ const needsTitle = sessionsRef.current.find((session) => session.id === sessionId)?.title === void 0;
54942
54341
  await engine.send(text, attachments);
54342
+ if (needsTitle) await refreshSessions();
54943
54343
  })().catch((e) => setError({ message: toErrorMessage(e) }));
54944
54344
  }, [
54945
54345
  engine,
@@ -54961,14 +54361,41 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
54961
54361
  const handleSelect = useCallback((id) => {
54962
54362
  engine.selectSession(id).then((history) => {
54963
54363
  setCurrentSessionId(id);
54964
- setMessages(history);
54364
+ setMessages(history.items);
54365
+ setMessagesCursor(history.nextCursor);
54965
54366
  setDrawerOpen(false);
54966
54367
  }).catch((e) => setError({ message: toErrorMessage(e) }));
54967
54368
  }, [engine]);
54369
+ /** 向历史方向再取一页:页内升序,所以是**前插**而不是追加 */
54370
+ const handleLoadEarlierMessages = useCallback(() => {
54371
+ if (!messagesCursor || loadingEarlierMessages) return;
54372
+ setLoadingEarlierMessages(true);
54373
+ engine.loadMoreMessages(messagesCursor).then((page) => {
54374
+ setMessages((prev) => [...page.items, ...prev]);
54375
+ setMessagesCursor(page.nextCursor);
54376
+ }).catch((e) => setError({ message: toErrorMessage(e) })).finally(() => setLoadingEarlierMessages(false));
54377
+ }, [
54378
+ engine,
54379
+ messagesCursor,
54380
+ loadingEarlierMessages
54381
+ ]);
54382
+ const handleLoadEarlierSessions = useCallback(() => {
54383
+ if (!sessionsCursor || loadingEarlierSessions) return;
54384
+ setLoadingEarlierSessions(true);
54385
+ engine.listSessions({ cursor: sessionsCursor }).then((page) => {
54386
+ setSessions((prev) => [...page.items, ...prev]);
54387
+ setSessionsCursor(page.nextCursor);
54388
+ }).catch((e) => setError({ message: toErrorMessage(e) })).finally(() => setLoadingEarlierSessions(false));
54389
+ }, [
54390
+ engine,
54391
+ sessionsCursor,
54392
+ loadingEarlierSessions
54393
+ ]);
54968
54394
  const handleCreate = useCallback(() => {
54969
54395
  engine.createSession().then(async (meta) => {
54970
54396
  setCurrentSessionId(meta.id);
54971
54397
  setMessages([]);
54398
+ setMessagesCursor(void 0);
54972
54399
  await refreshSessions();
54973
54400
  }).catch((e) => setError({ message: toErrorMessage(e) }));
54974
54401
  }, [engine, refreshSessions]);
@@ -55033,7 +54460,12 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
55033
54460
  "data-layout": resolvedLayout,
55034
54461
  className: `webskill-aui-shell flex h-full min-h-0 flex-row font-sans text-ink ${theme === "dark" ? "dark" : ""}`,
55035
54462
  children: [
55036
- /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(AssistantUiSessionList, { className: "hidden lg:flex" }), /* @__PURE__ */ jsx(Sheet, {
54463
+ /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(AssistantUiSessionList, {
54464
+ className: "hidden lg:flex",
54465
+ hasEarlier: sessionsCursor !== void 0,
54466
+ loadingEarlier: loadingEarlierSessions,
54467
+ onLoadEarlier: handleLoadEarlierSessions
54468
+ }), /* @__PURE__ */ jsx(Sheet, {
55037
54469
  open: drawerOpen,
55038
54470
  onOpenChange: setDrawerOpen,
55039
54471
  children: /* @__PURE__ */ jsx(SheetContent, {
@@ -55042,7 +54474,10 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
55042
54474
  className: "p-0 lg:hidden",
55043
54475
  children: /* @__PURE__ */ jsx(AssistantUiSessionList, {
55044
54476
  className: "h-full w-full border-r-0",
55045
- onNavigate: () => setDrawerOpen(false)
54477
+ onNavigate: () => setDrawerOpen(false),
54478
+ hasEarlier: sessionsCursor !== void 0,
54479
+ loadingEarlier: loadingEarlierSessions,
54480
+ onLoadEarlier: handleLoadEarlierSessions
55046
54481
  })
55047
54482
  })
55048
54483
  })] }),
@@ -55075,6 +54510,10 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
55075
54510
  }) : null,
55076
54511
  /* @__PURE__ */ jsx(AssistantUiMessageList, {
55077
54512
  messages,
54513
+ ...messagesCursor !== void 0 ? { earlier: {
54514
+ loading: loadingEarlierMessages,
54515
+ onLoad: handleLoadEarlierMessages
54516
+ } } : {},
55078
54517
  renderMessage: (message) => /* @__PURE__ */ jsx(AssistantUiMessage, {
55079
54518
  message,
55080
54519
  onRetry: handleRetry,
@@ -55087,18 +54526,14 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
55087
54526
  }),
55088
54527
  (() => {
55089
54528
  const interactionVisible = interactionSessionId === void 0 || interactionSessionId === currentSessionId;
55090
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
55091
- renderer === "a2ui" ? /* @__PURE__ */ jsx("div", {
55092
- ref: interactionMountRef,
55093
- "data-testid": "a2ui-interaction-mount",
55094
- className: interactionVisible && a2uiActive ? void 0 : "hidden"
55095
- }) : null,
55096
- renderer === "openui" ? /* @__PURE__ */ jsx("div", {
55097
- className: interactionVisible ? void 0 : "hidden",
55098
- children: /* @__PURE__ */ jsx(OpenUiInteraction, { channel: engine.openUiChannel })
55099
- }) : null,
55100
- interactionVisible ? renderer === "vercel" ? /* @__PURE__ */ jsx(VercelPayloadPreview, { bridge: engine.bridge }) : /* @__PURE__ */ jsx(InteractionCard, { bridge: engine.bridge }) : null
55101
- ] });
54529
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [renderer !== "native" ? /* @__PURE__ */ jsx("div", {
54530
+ "data-testid": "spec-interaction-slot",
54531
+ className: interactionVisible ? void 0 : "hidden",
54532
+ children: /* @__PURE__ */ jsx(SpecInteraction, {
54533
+ channel: engine.interactionChannel,
54534
+ renderer
54535
+ })
54536
+ }) : null, interactionVisible ? /* @__PURE__ */ jsx(InteractionCard, { bridge: engine.bridge }) : null] });
55102
54537
  })(),
55103
54538
  /* @__PURE__ */ jsx(ResultBlocksPro, {
55104
54539
  bridge: engine.bridge,
@@ -55112,13 +54547,11 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
55112
54547
  }) : null,
55113
54548
  renderer === "vercel" && currentRunId ? /* @__PURE__ */ jsx(VercelSurfaceHost, {
55114
54549
  bridge: engine.bridge,
55115
- runId: currentRunId,
55116
- ...surfaceRegistry ? { registry: surfaceRegistry } : {}
54550
+ runId: currentRunId
55117
54551
  }) : null,
55118
54552
  renderer === "openui" && currentRunId ? /* @__PURE__ */ jsx(OpenUiSurfaceHost, {
55119
54553
  bridge: engine.bridge,
55120
- runId: currentRunId,
55121
- ...surfaceRegistry ? { registry: surfaceRegistry } : {}
54554
+ runId: currentRunId
55122
54555
  }) : null,
55123
54556
  renderer === "a2ui" && currentRunId ? /* @__PURE__ */ jsx(A2uiSurfaceHost, {
55124
54557
  bridge: engine.bridge,
@@ -55160,4 +54593,52 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
55160
54593
  }
55161
54594
 
55162
54595
  //#endregion
55163
- export { A2uiSurfaceHost, A2uiSurfaceSnapshotHost, ChatEngine, Chatbot, CompositeUiBridge, ErrorCard, InteractionCard, InterruptedBanner, OpenUiChannel, OpenUiInteraction, OpenUiSurfaceHost, OpenUiSurfaceSnapshotHost, ResultBlockList, ResultBlocksPro, SettingsDrawer, SkillBadges, VercelPayloadPreview, VercelSurfaceHost, VercelSurfaceSnapshotHost, chatbotDictionary, configureA2uiMarkdown, probeA2uiAvailability, probeOpenUiAvailability, useT, writeRunTrace };
54596
+ //#region src/react/VercelPayloadPreview.tsx
54597
+ /**
54598
+ * vercel 档(payload 预览):不渲染表单,展示 InteractionRequest 经 toVercelToolInvocation
54599
+ * 转换后的 Vercel AI SDK 数据契约 JSON 卡。仅提供 Cancel 结束交互(非完整运行时)。
54600
+ * @experimental
54601
+ */
54602
+ function VercelPayloadPreview({ bridge }) {
54603
+ const t = useT();
54604
+ const pending = useSyncExternalStore(bridge.subscribe, () => bridge.pending);
54605
+ if (!pending) return null;
54606
+ const payload = toVercelToolInvocation(pending);
54607
+ return /* @__PURE__ */ jsxs("div", {
54608
+ "data-testid": "vercel-payload-preview",
54609
+ className: "rounded-card border border-border bg-surface shadow-card",
54610
+ children: [/* @__PURE__ */ jsxs("div", {
54611
+ className: "flex items-center gap-2 px-4 pt-4",
54612
+ children: [/* @__PURE__ */ jsx("h3", {
54613
+ className: "font-display text-sm font-semibold text-ink",
54614
+ children: t("vercel.preview.title")
54615
+ }), /* @__PURE__ */ jsx(Badge, {
54616
+ tone: "neutral",
54617
+ children: t("vercel.preview.badge")
54618
+ })]
54619
+ }), /* @__PURE__ */ jsxs("div", {
54620
+ className: "flex flex-col gap-3 px-4 py-4",
54621
+ children: [
54622
+ /* @__PURE__ */ jsx("p", {
54623
+ className: "text-xs text-muted",
54624
+ children: t("vercel.preview.description")
54625
+ }),
54626
+ /* @__PURE__ */ jsx("pre", {
54627
+ className: "max-h-64 overflow-auto rounded-lg border border-border bg-canvas p-3 font-mono text-xs text-ink",
54628
+ children: JSON.stringify(payload, null, 2)
54629
+ }),
54630
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, {
54631
+ variant: "secondary",
54632
+ onClick: () => bridge.resolve({
54633
+ id: pending.id,
54634
+ cancelled: true
54635
+ }),
54636
+ children: t("interaction.cancel")
54637
+ }) })
54638
+ ]
54639
+ })]
54640
+ });
54641
+ }
54642
+
54643
+ //#endregion
54644
+ export { A2uiSurfaceHost, A2uiSurfaceSnapshotHost, ChatEngine, Chatbot, CompositeUiBridge, DEFAULT_RENDERER_CAPABILITIES, ErrorCard, InteractionCard, InterruptedBanner, OpenUiSurfaceHost, OpenUiSurfaceSnapshotHost, ResultBlockList, ResultBlocksPro, SettingsDrawer, SkillBadges, SpecInteraction, VercelPayloadPreview, VercelSurfaceHost, VercelSurfaceSnapshotHost, chatbotDictionary, configureA2uiMarkdown, probeA2uiAvailability, probeOpenUiAvailability, useT };