@webskill/chatbot 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -15
- package/dist/chatbot.css +24 -0
- package/dist/index.d.ts +445 -28
- package/dist/index.js +1957 -549
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6,10 +6,11 @@ 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 {
|
|
10
|
-
import { BrowserWorkerScriptExecutor } from "@webskill/sdk/browser";
|
|
9
|
+
import { EventBus, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FullDisclosureRouter, SkillDiscovery, WebSkillError, WebSkillRuntime, assertRemoteUrlAllowed, clearStoredFormValues, isUnsupportedRunSnapshot, messageOf, summarizeToolCalls, textParts } from "@webskill/sdk";
|
|
10
|
+
import { BrowserWorkerScriptExecutor, checkDictationAvailability, createLlmClient, probeChromeBuiltinAvailability, startDictation } from "@webskill/sdk/browser";
|
|
11
11
|
import { JsonRenderSpecSurface, NativeSpecSurface, OpenUiSpecSurface, ReactBridgeState, SpecInteractionChannel, UiSurfaceList, UiSurfaceSnapshotList, probeJsonRenderAvailability } from "@webskill/sdk/ui-react";
|
|
12
12
|
import { A2UI_SPEC_FORM_PATH, createUiCatalogToolSource, fromA2uiSpecAction, fromA2uiSurfaceAction, interactionToFormModel, interactionToUiSpec, loadOpenUiPeers, loadWebSkillLitCatalog, renderMiniChart, shapeInteractionValue, toA2uiSpecMessages, toA2uiSurfaceAction, toVercelToolInvocation, uiCatalog } from "@webskill/sdk/ui";
|
|
13
|
+
import { TodoStore, createDelegationToolSource, createPagePerceptionToolSource, createSkillGenerationToolSource, createTodoToolSource, withDelegationOrigin } from "@webskill/sdk/agent";
|
|
13
14
|
import * as React$1 from "react";
|
|
14
15
|
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
16
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -191,7 +192,7 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
191
192
|
* This source code is licensed under the ISC license.
|
|
192
193
|
* See the LICENSE file in the root directory of this source tree.
|
|
193
194
|
*/
|
|
194
|
-
const __iconNode$
|
|
195
|
+
const __iconNode$36 = [
|
|
195
196
|
["rect", {
|
|
196
197
|
width: "20",
|
|
197
198
|
height: "5",
|
|
@@ -217,7 +218,7 @@ const __iconNode$33 = [
|
|
|
217
218
|
key: "192myk"
|
|
218
219
|
}]
|
|
219
220
|
];
|
|
220
|
-
const ArchiveRestore = createLucideIcon("archive-restore", __iconNode$
|
|
221
|
+
const ArchiveRestore = createLucideIcon("archive-restore", __iconNode$36);
|
|
221
222
|
|
|
222
223
|
//#endregion
|
|
223
224
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/archive.mjs
|
|
@@ -227,7 +228,7 @@ const ArchiveRestore = createLucideIcon("archive-restore", __iconNode$33);
|
|
|
227
228
|
* This source code is licensed under the ISC license.
|
|
228
229
|
* See the LICENSE file in the root directory of this source tree.
|
|
229
230
|
*/
|
|
230
|
-
const __iconNode$
|
|
231
|
+
const __iconNode$35 = [
|
|
231
232
|
["rect", {
|
|
232
233
|
width: "20",
|
|
233
234
|
height: "5",
|
|
@@ -245,7 +246,7 @@ const __iconNode$32 = [
|
|
|
245
246
|
key: "a56b0p"
|
|
246
247
|
}]
|
|
247
248
|
];
|
|
248
|
-
const Archive = createLucideIcon("archive", __iconNode$
|
|
249
|
+
const Archive = createLucideIcon("archive", __iconNode$35);
|
|
249
250
|
|
|
250
251
|
//#endregion
|
|
251
252
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/arrow-up.mjs
|
|
@@ -255,14 +256,14 @@ const Archive = createLucideIcon("archive", __iconNode$32);
|
|
|
255
256
|
* This source code is licensed under the ISC license.
|
|
256
257
|
* See the LICENSE file in the root directory of this source tree.
|
|
257
258
|
*/
|
|
258
|
-
const __iconNode$
|
|
259
|
+
const __iconNode$34 = [["path", {
|
|
259
260
|
d: "m5 12 7-7 7 7",
|
|
260
261
|
key: "hav0vg"
|
|
261
262
|
}], ["path", {
|
|
262
263
|
d: "M12 19V5",
|
|
263
264
|
key: "x0mq9r"
|
|
264
265
|
}]];
|
|
265
|
-
const ArrowUp = createLucideIcon("arrow-up", __iconNode$
|
|
266
|
+
const ArrowUp = createLucideIcon("arrow-up", __iconNode$34);
|
|
266
267
|
|
|
267
268
|
//#endregion
|
|
268
269
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/check.mjs
|
|
@@ -272,11 +273,11 @@ const ArrowUp = createLucideIcon("arrow-up", __iconNode$31);
|
|
|
272
273
|
* This source code is licensed under the ISC license.
|
|
273
274
|
* See the LICENSE file in the root directory of this source tree.
|
|
274
275
|
*/
|
|
275
|
-
const __iconNode$
|
|
276
|
+
const __iconNode$33 = [["path", {
|
|
276
277
|
d: "M20 6 9 17l-5-5",
|
|
277
278
|
key: "1gmf2c"
|
|
278
279
|
}]];
|
|
279
|
-
const Check = createLucideIcon("check", __iconNode$
|
|
280
|
+
const Check = createLucideIcon("check", __iconNode$33);
|
|
280
281
|
|
|
281
282
|
//#endregion
|
|
282
283
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/chevron-down.mjs
|
|
@@ -286,11 +287,11 @@ const Check = createLucideIcon("check", __iconNode$30);
|
|
|
286
287
|
* This source code is licensed under the ISC license.
|
|
287
288
|
* See the LICENSE file in the root directory of this source tree.
|
|
288
289
|
*/
|
|
289
|
-
const __iconNode$
|
|
290
|
+
const __iconNode$32 = [["path", {
|
|
290
291
|
d: "m6 9 6 6 6-6",
|
|
291
292
|
key: "qrunsl"
|
|
292
293
|
}]];
|
|
293
|
-
const ChevronDown = createLucideIcon("chevron-down", __iconNode$
|
|
294
|
+
const ChevronDown = createLucideIcon("chevron-down", __iconNode$32);
|
|
294
295
|
|
|
295
296
|
//#endregion
|
|
296
297
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/circle-check.mjs
|
|
@@ -300,7 +301,7 @@ const ChevronDown = createLucideIcon("chevron-down", __iconNode$29);
|
|
|
300
301
|
* This source code is licensed under the ISC license.
|
|
301
302
|
* See the LICENSE file in the root directory of this source tree.
|
|
302
303
|
*/
|
|
303
|
-
const __iconNode$
|
|
304
|
+
const __iconNode$31 = [["circle", {
|
|
304
305
|
cx: "12",
|
|
305
306
|
cy: "12",
|
|
306
307
|
r: "10",
|
|
@@ -309,7 +310,7 @@ const __iconNode$28 = [["circle", {
|
|
|
309
310
|
d: "m9 12 2 2 4-4",
|
|
310
311
|
key: "dzmm74"
|
|
311
312
|
}]];
|
|
312
|
-
const CircleCheck = createLucideIcon("circle-check", __iconNode$
|
|
313
|
+
const CircleCheck = createLucideIcon("circle-check", __iconNode$31);
|
|
313
314
|
|
|
314
315
|
//#endregion
|
|
315
316
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/circle-question-mark.mjs
|
|
@@ -319,7 +320,7 @@ const CircleCheck = createLucideIcon("circle-check", __iconNode$28);
|
|
|
319
320
|
* This source code is licensed under the ISC license.
|
|
320
321
|
* See the LICENSE file in the root directory of this source tree.
|
|
321
322
|
*/
|
|
322
|
-
const __iconNode$
|
|
323
|
+
const __iconNode$30 = [
|
|
323
324
|
["circle", {
|
|
324
325
|
cx: "12",
|
|
325
326
|
cy: "12",
|
|
@@ -335,7 +336,7 @@ const __iconNode$27 = [
|
|
|
335
336
|
key: "p32p05"
|
|
336
337
|
}]
|
|
337
338
|
];
|
|
338
|
-
const CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode$
|
|
339
|
+
const CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode$30);
|
|
339
340
|
|
|
340
341
|
//#endregion
|
|
341
342
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/circle-x.mjs
|
|
@@ -345,7 +346,7 @@ const CircleQuestionMark = createLucideIcon("circle-question-mark", __iconNode$2
|
|
|
345
346
|
* This source code is licensed under the ISC license.
|
|
346
347
|
* See the LICENSE file in the root directory of this source tree.
|
|
347
348
|
*/
|
|
348
|
-
const __iconNode$
|
|
349
|
+
const __iconNode$29 = [
|
|
349
350
|
["circle", {
|
|
350
351
|
cx: "12",
|
|
351
352
|
cy: "12",
|
|
@@ -361,7 +362,7 @@ const __iconNode$26 = [
|
|
|
361
362
|
key: "z0biqf"
|
|
362
363
|
}]
|
|
363
364
|
];
|
|
364
|
-
const CircleX = createLucideIcon("circle-x", __iconNode$
|
|
365
|
+
const CircleX = createLucideIcon("circle-x", __iconNode$29);
|
|
365
366
|
|
|
366
367
|
//#endregion
|
|
367
368
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/clipboard-list.mjs
|
|
@@ -371,7 +372,7 @@ const CircleX = createLucideIcon("circle-x", __iconNode$26);
|
|
|
371
372
|
* This source code is licensed under the ISC license.
|
|
372
373
|
* See the LICENSE file in the root directory of this source tree.
|
|
373
374
|
*/
|
|
374
|
-
const __iconNode$
|
|
375
|
+
const __iconNode$28 = [
|
|
375
376
|
["rect", {
|
|
376
377
|
width: "8",
|
|
377
378
|
height: "4",
|
|
@@ -402,7 +403,7 @@ const __iconNode$25 = [
|
|
|
402
403
|
key: "18s6g9"
|
|
403
404
|
}]
|
|
404
405
|
];
|
|
405
|
-
const ClipboardList = createLucideIcon("clipboard-list", __iconNode$
|
|
406
|
+
const ClipboardList = createLucideIcon("clipboard-list", __iconNode$28);
|
|
406
407
|
|
|
407
408
|
//#endregion
|
|
408
409
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/copy.mjs
|
|
@@ -412,7 +413,7 @@ const ClipboardList = createLucideIcon("clipboard-list", __iconNode$25);
|
|
|
412
413
|
* This source code is licensed under the ISC license.
|
|
413
414
|
* See the LICENSE file in the root directory of this source tree.
|
|
414
415
|
*/
|
|
415
|
-
const __iconNode$
|
|
416
|
+
const __iconNode$27 = [["rect", {
|
|
416
417
|
width: "14",
|
|
417
418
|
height: "14",
|
|
418
419
|
x: "8",
|
|
@@ -424,7 +425,7 @@ const __iconNode$24 = [["rect", {
|
|
|
424
425
|
d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",
|
|
425
426
|
key: "zix9uf"
|
|
426
427
|
}]];
|
|
427
|
-
const Copy = createLucideIcon("copy", __iconNode$
|
|
428
|
+
const Copy = createLucideIcon("copy", __iconNode$27);
|
|
428
429
|
|
|
429
430
|
//#endregion
|
|
430
431
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/download.mjs
|
|
@@ -434,7 +435,7 @@ const Copy = createLucideIcon("copy", __iconNode$24);
|
|
|
434
435
|
* This source code is licensed under the ISC license.
|
|
435
436
|
* See the LICENSE file in the root directory of this source tree.
|
|
436
437
|
*/
|
|
437
|
-
const __iconNode$
|
|
438
|
+
const __iconNode$26 = [
|
|
438
439
|
["path", {
|
|
439
440
|
d: "M12 15V3",
|
|
440
441
|
key: "m9g1x1"
|
|
@@ -448,7 +449,7 @@ const __iconNode$23 = [
|
|
|
448
449
|
key: "brsn70"
|
|
449
450
|
}]
|
|
450
451
|
];
|
|
451
|
-
const Download = createLucideIcon("download", __iconNode$
|
|
452
|
+
const Download = createLucideIcon("download", __iconNode$26);
|
|
452
453
|
|
|
453
454
|
//#endregion
|
|
454
455
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/external-link.mjs
|
|
@@ -458,7 +459,7 @@ const Download = createLucideIcon("download", __iconNode$23);
|
|
|
458
459
|
* This source code is licensed under the ISC license.
|
|
459
460
|
* See the LICENSE file in the root directory of this source tree.
|
|
460
461
|
*/
|
|
461
|
-
const __iconNode$
|
|
462
|
+
const __iconNode$25 = [
|
|
462
463
|
["path", {
|
|
463
464
|
d: "M15 3h6v6",
|
|
464
465
|
key: "1q9fwt"
|
|
@@ -472,7 +473,26 @@ const __iconNode$22 = [
|
|
|
472
473
|
key: "a6xqqp"
|
|
473
474
|
}]
|
|
474
475
|
];
|
|
475
|
-
const ExternalLink = createLucideIcon("external-link", __iconNode$
|
|
476
|
+
const ExternalLink = createLucideIcon("external-link", __iconNode$25);
|
|
477
|
+
|
|
478
|
+
//#endregion
|
|
479
|
+
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/eye.mjs
|
|
480
|
+
/**
|
|
481
|
+
* @license lucide-react v1.27.0 - ISC
|
|
482
|
+
*
|
|
483
|
+
* This source code is licensed under the ISC license.
|
|
484
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
485
|
+
*/
|
|
486
|
+
const __iconNode$24 = [["path", {
|
|
487
|
+
d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",
|
|
488
|
+
key: "1nclc0"
|
|
489
|
+
}], ["circle", {
|
|
490
|
+
cx: "12",
|
|
491
|
+
cy: "12",
|
|
492
|
+
r: "3",
|
|
493
|
+
key: "1v7zrd"
|
|
494
|
+
}]];
|
|
495
|
+
const Eye = createLucideIcon("eye", __iconNode$24);
|
|
476
496
|
|
|
477
497
|
//#endregion
|
|
478
498
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/file.mjs
|
|
@@ -482,14 +502,14 @@ const ExternalLink = createLucideIcon("external-link", __iconNode$22);
|
|
|
482
502
|
* This source code is licensed under the ISC license.
|
|
483
503
|
* See the LICENSE file in the root directory of this source tree.
|
|
484
504
|
*/
|
|
485
|
-
const __iconNode$
|
|
505
|
+
const __iconNode$23 = [["path", {
|
|
486
506
|
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",
|
|
487
507
|
key: "1oefj6"
|
|
488
508
|
}], ["path", {
|
|
489
509
|
d: "M14 2v5a1 1 0 0 0 1 1h5",
|
|
490
510
|
key: "wfsgrz"
|
|
491
511
|
}]];
|
|
492
|
-
const File = createLucideIcon("file", __iconNode$
|
|
512
|
+
const File = createLucideIcon("file", __iconNode$23);
|
|
493
513
|
|
|
494
514
|
//#endregion
|
|
495
515
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/list-checks.mjs
|
|
@@ -499,7 +519,7 @@ const File = createLucideIcon("file", __iconNode$21);
|
|
|
499
519
|
* This source code is licensed under the ISC license.
|
|
500
520
|
* See the LICENSE file in the root directory of this source tree.
|
|
501
521
|
*/
|
|
502
|
-
const __iconNode$
|
|
522
|
+
const __iconNode$22 = [
|
|
503
523
|
["path", {
|
|
504
524
|
d: "M13 5h8",
|
|
505
525
|
key: "a7qcls"
|
|
@@ -521,7 +541,7 @@ const __iconNode$20 = [
|
|
|
521
541
|
key: "1obspn"
|
|
522
542
|
}]
|
|
523
543
|
];
|
|
524
|
-
const ListChecks = createLucideIcon("list-checks", __iconNode$
|
|
544
|
+
const ListChecks = createLucideIcon("list-checks", __iconNode$22);
|
|
525
545
|
|
|
526
546
|
//#endregion
|
|
527
547
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/loader-circle.mjs
|
|
@@ -531,11 +551,11 @@ const ListChecks = createLucideIcon("list-checks", __iconNode$20);
|
|
|
531
551
|
* This source code is licensed under the ISC license.
|
|
532
552
|
* See the LICENSE file in the root directory of this source tree.
|
|
533
553
|
*/
|
|
534
|
-
const __iconNode$
|
|
554
|
+
const __iconNode$21 = [["path", {
|
|
535
555
|
d: "M21 12a9 9 0 1 1-6.219-8.56",
|
|
536
556
|
key: "13zald"
|
|
537
557
|
}]];
|
|
538
|
-
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$
|
|
558
|
+
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$21);
|
|
539
559
|
|
|
540
560
|
//#endregion
|
|
541
561
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/menu.mjs
|
|
@@ -545,7 +565,7 @@ const LoaderCircle = createLucideIcon("loader-circle", __iconNode$19);
|
|
|
545
565
|
* This source code is licensed under the ISC license.
|
|
546
566
|
* See the LICENSE file in the root directory of this source tree.
|
|
547
567
|
*/
|
|
548
|
-
const __iconNode$
|
|
568
|
+
const __iconNode$20 = [
|
|
549
569
|
["path", {
|
|
550
570
|
d: "M4 5h16",
|
|
551
571
|
key: "1tepv9"
|
|
@@ -559,7 +579,7 @@ const __iconNode$18 = [
|
|
|
559
579
|
key: "1djgab"
|
|
560
580
|
}]
|
|
561
581
|
];
|
|
562
|
-
const Menu = createLucideIcon("menu", __iconNode$
|
|
582
|
+
const Menu = createLucideIcon("menu", __iconNode$20);
|
|
563
583
|
|
|
564
584
|
//#endregion
|
|
565
585
|
//#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
|
|
@@ -569,7 +589,7 @@ const Menu = createLucideIcon("menu", __iconNode$18);
|
|
|
569
589
|
* This source code is licensed under the ISC license.
|
|
570
590
|
* See the LICENSE file in the root directory of this source tree.
|
|
571
591
|
*/
|
|
572
|
-
const __iconNode$
|
|
592
|
+
const __iconNode$19 = [
|
|
573
593
|
["path", {
|
|
574
594
|
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",
|
|
575
595
|
key: "1sd12s"
|
|
@@ -583,7 +603,35 @@ const __iconNode$17 = [
|
|
|
583
603
|
key: "p32p05"
|
|
584
604
|
}]
|
|
585
605
|
];
|
|
586
|
-
const MessageCircleQuestionMark = createLucideIcon("message-circle-question-mark", __iconNode$
|
|
606
|
+
const MessageCircleQuestionMark = createLucideIcon("message-circle-question-mark", __iconNode$19);
|
|
607
|
+
|
|
608
|
+
//#endregion
|
|
609
|
+
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/mic.mjs
|
|
610
|
+
/**
|
|
611
|
+
* @license lucide-react v1.27.0 - ISC
|
|
612
|
+
*
|
|
613
|
+
* This source code is licensed under the ISC license.
|
|
614
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
615
|
+
*/
|
|
616
|
+
const __iconNode$18 = [
|
|
617
|
+
["path", {
|
|
618
|
+
d: "M12 19v3",
|
|
619
|
+
key: "npa21l"
|
|
620
|
+
}],
|
|
621
|
+
["path", {
|
|
622
|
+
d: "M19 10v2a7 7 0 0 1-14 0v-2",
|
|
623
|
+
key: "1vc78b"
|
|
624
|
+
}],
|
|
625
|
+
["rect", {
|
|
626
|
+
x: "9",
|
|
627
|
+
y: "2",
|
|
628
|
+
width: "6",
|
|
629
|
+
height: "13",
|
|
630
|
+
rx: "3",
|
|
631
|
+
key: "s6n7sd"
|
|
632
|
+
}]
|
|
633
|
+
];
|
|
634
|
+
const Mic = createLucideIcon("mic", __iconNode$18);
|
|
587
635
|
|
|
588
636
|
//#endregion
|
|
589
637
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/panel-right-open.mjs
|
|
@@ -593,7 +641,7 @@ const MessageCircleQuestionMark = createLucideIcon("message-circle-question-mark
|
|
|
593
641
|
* This source code is licensed under the ISC license.
|
|
594
642
|
* See the LICENSE file in the root directory of this source tree.
|
|
595
643
|
*/
|
|
596
|
-
const __iconNode$
|
|
644
|
+
const __iconNode$17 = [
|
|
597
645
|
["rect", {
|
|
598
646
|
width: "18",
|
|
599
647
|
height: "18",
|
|
@@ -611,7 +659,7 @@ const __iconNode$16 = [
|
|
|
611
659
|
key: "1pgupc"
|
|
612
660
|
}]
|
|
613
661
|
];
|
|
614
|
-
const PanelRightOpen = createLucideIcon("panel-right-open", __iconNode$
|
|
662
|
+
const PanelRightOpen = createLucideIcon("panel-right-open", __iconNode$17);
|
|
615
663
|
|
|
616
664
|
//#endregion
|
|
617
665
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/paperclip.mjs
|
|
@@ -621,11 +669,11 @@ const PanelRightOpen = createLucideIcon("panel-right-open", __iconNode$16);
|
|
|
621
669
|
* This source code is licensed under the ISC license.
|
|
622
670
|
* See the LICENSE file in the root directory of this source tree.
|
|
623
671
|
*/
|
|
624
|
-
const __iconNode$
|
|
672
|
+
const __iconNode$16 = [["path", {
|
|
625
673
|
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",
|
|
626
674
|
key: "1miecu"
|
|
627
675
|
}]];
|
|
628
|
-
const Paperclip = createLucideIcon("paperclip", __iconNode$
|
|
676
|
+
const Paperclip = createLucideIcon("paperclip", __iconNode$16);
|
|
629
677
|
|
|
630
678
|
//#endregion
|
|
631
679
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/pencil.mjs
|
|
@@ -635,14 +683,14 @@ const Paperclip = createLucideIcon("paperclip", __iconNode$15);
|
|
|
635
683
|
* This source code is licensed under the ISC license.
|
|
636
684
|
* See the LICENSE file in the root directory of this source tree.
|
|
637
685
|
*/
|
|
638
|
-
const __iconNode$
|
|
686
|
+
const __iconNode$15 = [["path", {
|
|
639
687
|
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
688
|
key: "1a8usu"
|
|
641
689
|
}], ["path", {
|
|
642
690
|
d: "m15 5 4 4",
|
|
643
691
|
key: "1mk7zo"
|
|
644
692
|
}]];
|
|
645
|
-
const Pencil = createLucideIcon("pencil", __iconNode$
|
|
693
|
+
const Pencil = createLucideIcon("pencil", __iconNode$15);
|
|
646
694
|
|
|
647
695
|
//#endregion
|
|
648
696
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/play.mjs
|
|
@@ -652,11 +700,11 @@ const Pencil = createLucideIcon("pencil", __iconNode$14);
|
|
|
652
700
|
* This source code is licensed under the ISC license.
|
|
653
701
|
* See the LICENSE file in the root directory of this source tree.
|
|
654
702
|
*/
|
|
655
|
-
const __iconNode$
|
|
703
|
+
const __iconNode$14 = [["path", {
|
|
656
704
|
d: "M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",
|
|
657
705
|
key: "10ikf1"
|
|
658
706
|
}]];
|
|
659
|
-
const Play = createLucideIcon("play", __iconNode$
|
|
707
|
+
const Play = createLucideIcon("play", __iconNode$14);
|
|
660
708
|
|
|
661
709
|
//#endregion
|
|
662
710
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/plug-zap.mjs
|
|
@@ -666,7 +714,7 @@ const Play = createLucideIcon("play", __iconNode$13);
|
|
|
666
714
|
* This source code is licensed under the ISC license.
|
|
667
715
|
* See the LICENSE file in the root directory of this source tree.
|
|
668
716
|
*/
|
|
669
|
-
const __iconNode$
|
|
717
|
+
const __iconNode$13 = [
|
|
670
718
|
["path", {
|
|
671
719
|
d: "M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z",
|
|
672
720
|
key: "goz73y"
|
|
@@ -688,7 +736,7 @@ const __iconNode$12 = [
|
|
|
688
736
|
key: "16psg9"
|
|
689
737
|
}]
|
|
690
738
|
];
|
|
691
|
-
const PlugZap = createLucideIcon("plug-zap", __iconNode$
|
|
739
|
+
const PlugZap = createLucideIcon("plug-zap", __iconNode$13);
|
|
692
740
|
|
|
693
741
|
//#endregion
|
|
694
742
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/plus.mjs
|
|
@@ -698,14 +746,14 @@ const PlugZap = createLucideIcon("plug-zap", __iconNode$12);
|
|
|
698
746
|
* This source code is licensed under the ISC license.
|
|
699
747
|
* See the LICENSE file in the root directory of this source tree.
|
|
700
748
|
*/
|
|
701
|
-
const __iconNode$
|
|
749
|
+
const __iconNode$12 = [["path", {
|
|
702
750
|
d: "M5 12h14",
|
|
703
751
|
key: "1ays0h"
|
|
704
752
|
}], ["path", {
|
|
705
753
|
d: "M12 5v14",
|
|
706
754
|
key: "s699le"
|
|
707
755
|
}]];
|
|
708
|
-
const Plus = createLucideIcon("plus", __iconNode$
|
|
756
|
+
const Plus = createLucideIcon("plus", __iconNode$12);
|
|
709
757
|
|
|
710
758
|
//#endregion
|
|
711
759
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/rotate-ccw-clock.mjs
|
|
@@ -715,7 +763,7 @@ const Plus = createLucideIcon("plus", __iconNode$11);
|
|
|
715
763
|
* This source code is licensed under the ISC license.
|
|
716
764
|
* See the LICENSE file in the root directory of this source tree.
|
|
717
765
|
*/
|
|
718
|
-
const __iconNode$
|
|
766
|
+
const __iconNode$11 = [
|
|
719
767
|
["path", {
|
|
720
768
|
d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",
|
|
721
769
|
key: "1357e3"
|
|
@@ -729,7 +777,7 @@ const __iconNode$10 = [
|
|
|
729
777
|
key: "1fdv2h"
|
|
730
778
|
}]
|
|
731
779
|
];
|
|
732
|
-
const RotateCcwClock = createLucideIcon("rotate-ccw-clock", __iconNode$
|
|
780
|
+
const RotateCcwClock = createLucideIcon("rotate-ccw-clock", __iconNode$11);
|
|
733
781
|
|
|
734
782
|
//#endregion
|
|
735
783
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/rotate-ccw.mjs
|
|
@@ -739,14 +787,14 @@ const RotateCcwClock = createLucideIcon("rotate-ccw-clock", __iconNode$10);
|
|
|
739
787
|
* This source code is licensed under the ISC license.
|
|
740
788
|
* See the LICENSE file in the root directory of this source tree.
|
|
741
789
|
*/
|
|
742
|
-
const __iconNode$
|
|
790
|
+
const __iconNode$10 = [["path", {
|
|
743
791
|
d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",
|
|
744
792
|
key: "1357e3"
|
|
745
793
|
}], ["path", {
|
|
746
794
|
d: "M3 3v5h5",
|
|
747
795
|
key: "1xhq8a"
|
|
748
796
|
}]];
|
|
749
|
-
const RotateCcw = createLucideIcon("rotate-ccw", __iconNode$
|
|
797
|
+
const RotateCcw = createLucideIcon("rotate-ccw", __iconNode$10);
|
|
750
798
|
|
|
751
799
|
//#endregion
|
|
752
800
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/search.mjs
|
|
@@ -756,7 +804,7 @@ const RotateCcw = createLucideIcon("rotate-ccw", __iconNode$9);
|
|
|
756
804
|
* This source code is licensed under the ISC license.
|
|
757
805
|
* See the LICENSE file in the root directory of this source tree.
|
|
758
806
|
*/
|
|
759
|
-
const __iconNode$
|
|
807
|
+
const __iconNode$9 = [["path", {
|
|
760
808
|
d: "m21 21-4.34-4.34",
|
|
761
809
|
key: "14j7rj"
|
|
762
810
|
}], ["circle", {
|
|
@@ -765,7 +813,7 @@ const __iconNode$8 = [["path", {
|
|
|
765
813
|
r: "8",
|
|
766
814
|
key: "4ej97u"
|
|
767
815
|
}]];
|
|
768
|
-
const Search = createLucideIcon("search", __iconNode$
|
|
816
|
+
const Search = createLucideIcon("search", __iconNode$9);
|
|
769
817
|
|
|
770
818
|
//#endregion
|
|
771
819
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/settings.mjs
|
|
@@ -775,7 +823,7 @@ const Search = createLucideIcon("search", __iconNode$8);
|
|
|
775
823
|
* This source code is licensed under the ISC license.
|
|
776
824
|
* See the LICENSE file in the root directory of this source tree.
|
|
777
825
|
*/
|
|
778
|
-
const __iconNode$
|
|
826
|
+
const __iconNode$8 = [["path", {
|
|
779
827
|
d: "M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",
|
|
780
828
|
key: "1i5ecw"
|
|
781
829
|
}], ["circle", {
|
|
@@ -784,7 +832,7 @@ const __iconNode$7 = [["path", {
|
|
|
784
832
|
r: "3",
|
|
785
833
|
key: "1v7zrd"
|
|
786
834
|
}]];
|
|
787
|
-
const Settings = createLucideIcon("settings", __iconNode$
|
|
835
|
+
const Settings = createLucideIcon("settings", __iconNode$8);
|
|
788
836
|
|
|
789
837
|
//#endregion
|
|
790
838
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/shield-alert.mjs
|
|
@@ -794,7 +842,7 @@ const Settings = createLucideIcon("settings", __iconNode$7);
|
|
|
794
842
|
* This source code is licensed under the ISC license.
|
|
795
843
|
* See the LICENSE file in the root directory of this source tree.
|
|
796
844
|
*/
|
|
797
|
-
const __iconNode$
|
|
845
|
+
const __iconNode$7 = [
|
|
798
846
|
["path", {
|
|
799
847
|
d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",
|
|
800
848
|
key: "oel41y"
|
|
@@ -808,7 +856,31 @@ const __iconNode$6 = [
|
|
|
808
856
|
key: "1drbdi"
|
|
809
857
|
}]
|
|
810
858
|
];
|
|
811
|
-
const ShieldAlert = createLucideIcon("shield-alert", __iconNode$
|
|
859
|
+
const ShieldAlert = createLucideIcon("shield-alert", __iconNode$7);
|
|
860
|
+
|
|
861
|
+
//#endregion
|
|
862
|
+
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/shield-off.mjs
|
|
863
|
+
/**
|
|
864
|
+
* @license lucide-react v1.27.0 - ISC
|
|
865
|
+
*
|
|
866
|
+
* This source code is licensed under the ISC license.
|
|
867
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
868
|
+
*/
|
|
869
|
+
const __iconNode$6 = [
|
|
870
|
+
["path", {
|
|
871
|
+
d: "m2 2 20 20",
|
|
872
|
+
key: "1ooewy"
|
|
873
|
+
}],
|
|
874
|
+
["path", {
|
|
875
|
+
d: "M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71",
|
|
876
|
+
key: "1jlk70"
|
|
877
|
+
}],
|
|
878
|
+
["path", {
|
|
879
|
+
d: "M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264",
|
|
880
|
+
key: "18rp1v"
|
|
881
|
+
}]
|
|
882
|
+
];
|
|
883
|
+
const ShieldOff = createLucideIcon("shield-off", __iconNode$6);
|
|
812
884
|
|
|
813
885
|
//#endregion
|
|
814
886
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/sparkles.mjs
|
|
@@ -9030,7 +9102,6 @@ function cn(...inputs) {
|
|
|
9030
9102
|
//#region ../ui-kit/src/components/Badge.tsx
|
|
9031
9103
|
const toneClasses = {
|
|
9032
9104
|
neutral: "bg-subtle text-muted",
|
|
9033
|
-
primary: "bg-subtle text-ink",
|
|
9034
9105
|
success: "bg-success-soft text-success",
|
|
9035
9106
|
warning: "bg-warning-soft text-warning",
|
|
9036
9107
|
info: "bg-info-soft text-info",
|
|
@@ -9628,6 +9699,7 @@ function Select({ options, value, onChange, placeholder, disabled = false, id, l
|
|
|
9628
9699
|
children: placeholder
|
|
9629
9700
|
}), options.map((option) => /* @__PURE__ */ jsx("option", {
|
|
9630
9701
|
value: option.value,
|
|
9702
|
+
disabled: option.disabled,
|
|
9631
9703
|
children: option.label
|
|
9632
9704
|
}, option.value))]
|
|
9633
9705
|
}), /* @__PURE__ */ jsx(ChevronDown, {
|
|
@@ -40531,6 +40603,18 @@ const fadeInUp = {
|
|
|
40531
40603
|
|
|
40532
40604
|
//#endregion
|
|
40533
40605
|
//#region ../ui-kit/src/runtime-config/types.ts
|
|
40606
|
+
/**
|
|
40607
|
+
* 生成式 UI 场景预设的默认开放范围。
|
|
40608
|
+
* ui-kit 不依赖 `@webskill/ui`,因此这里只能写字面量;
|
|
40609
|
+
* 与目录实际导出的 `UI_PRESET_NAMES` 是否一致由 console 的测试守护。
|
|
40610
|
+
*/
|
|
40611
|
+
const DEFAULT_UI_PRESETS = [
|
|
40612
|
+
"charts",
|
|
40613
|
+
"cards",
|
|
40614
|
+
"dashboards",
|
|
40615
|
+
"slides",
|
|
40616
|
+
"reports"
|
|
40617
|
+
];
|
|
40534
40618
|
/** SDK 默认值(AgentLoop 10/120s/30s/100KB、interaction user/required/300s、deny-all 等) */
|
|
40535
40619
|
function defaultRuntimeConfig() {
|
|
40536
40620
|
return {
|
|
@@ -40565,13 +40649,60 @@ function defaultRuntimeConfig() {
|
|
|
40565
40649
|
remoteUrl: {
|
|
40566
40650
|
allowHttp: false,
|
|
40567
40651
|
allowPrivateHosts: false
|
|
40568
|
-
}
|
|
40652
|
+
},
|
|
40653
|
+
typescript: { enabled: false }
|
|
40569
40654
|
},
|
|
40570
|
-
llm: {
|
|
40571
|
-
|
|
40572
|
-
|
|
40573
|
-
|
|
40574
|
-
|
|
40655
|
+
llm: { entries: [] },
|
|
40656
|
+
agentCapabilities: {
|
|
40657
|
+
todo: true,
|
|
40658
|
+
skillGeneration: false,
|
|
40659
|
+
generativeUi: false,
|
|
40660
|
+
formAutofill: false,
|
|
40661
|
+
delegation: false,
|
|
40662
|
+
uiPresets: [...DEFAULT_UI_PRESETS]
|
|
40663
|
+
},
|
|
40664
|
+
security: { unsignedSkills: "warn" }
|
|
40665
|
+
};
|
|
40666
|
+
}
|
|
40667
|
+
/** 旧的单对象配置升格成列表时使用的固定 id:每次 load 都重算,随机 id 会让会话里存的归属失效 */
|
|
40668
|
+
const MIGRATED_ENTRY_ID = "llm-1";
|
|
40669
|
+
function isLlmEntry(value) {
|
|
40670
|
+
const e = value;
|
|
40671
|
+
return typeof e?.id === "string" && e.id !== "" && typeof e.provider === "string";
|
|
40672
|
+
}
|
|
40673
|
+
/**
|
|
40674
|
+
* `llm` 段的迁移(AC-4.2):旧的单对象升格为单元素列表,原字段一个不丢(含 `apiKey`)。
|
|
40675
|
+
* 全空的旧配置等价于「还没配过模型」,不生成占位条目。
|
|
40676
|
+
*/
|
|
40677
|
+
function mergeLlmSelection(raw) {
|
|
40678
|
+
const value = typeof raw === "object" && raw !== null ? raw : {};
|
|
40679
|
+
const entries = Array.isArray(value["entries"]) ? value["entries"].filter(isLlmEntry).map((entry) => ({
|
|
40680
|
+
...entry,
|
|
40681
|
+
label: entry.label ?? entry.model ?? entry.id
|
|
40682
|
+
})) : upgradeLegacyLlm(value);
|
|
40683
|
+
if (entries.length === 0) return { entries: [] };
|
|
40684
|
+
const requested = typeof value["defaultId"] === "string" ? value["defaultId"] : void 0;
|
|
40685
|
+
const defaultId = entries.some((entry) => entry.id === requested) ? requested : entries[0]?.id;
|
|
40686
|
+
return defaultId !== void 0 ? {
|
|
40687
|
+
entries,
|
|
40688
|
+
defaultId
|
|
40689
|
+
} : { entries };
|
|
40690
|
+
}
|
|
40691
|
+
function upgradeLegacyLlm(legacy) {
|
|
40692
|
+
const model = typeof legacy.model === "string" ? legacy.model.trim() : "";
|
|
40693
|
+
const baseUrl = typeof legacy.baseUrl === "string" ? legacy.baseUrl.trim() : "";
|
|
40694
|
+
const apiKey = typeof legacy.apiKey === "string" ? legacy.apiKey.trim() : "";
|
|
40695
|
+
if (model === "" && baseUrl === "" && apiKey === "") return [];
|
|
40696
|
+
const provider = typeof legacy.provider === "string" ? legacy.provider : "openai-compatible";
|
|
40697
|
+
return [{
|
|
40698
|
+
id: MIGRATED_ENTRY_ID,
|
|
40699
|
+
label: model !== "" ? model : provider,
|
|
40700
|
+
provider,
|
|
40701
|
+
model,
|
|
40702
|
+
...baseUrl !== "" ? { baseUrl } : {},
|
|
40703
|
+
...apiKey !== "" ? { apiKey } : {},
|
|
40704
|
+
...typeof legacy.requestTimeoutMs === "number" ? { requestTimeoutMs: legacy.requestTimeoutMs } : {}
|
|
40705
|
+
}];
|
|
40575
40706
|
}
|
|
40576
40707
|
/** 存储读出的部分配置按默认值补齐(老版本存储缺字段时平滑迁移) */
|
|
40577
40708
|
function mergeRuntimeConfigDefaults(partial) {
|
|
@@ -40597,11 +40728,24 @@ function mergeRuntimeConfigDefaults(partial) {
|
|
|
40597
40728
|
remoteUrl: {
|
|
40598
40729
|
...d.sandbox.remoteUrl,
|
|
40599
40730
|
...p["sandbox"]?.["remoteUrl"] ?? {}
|
|
40731
|
+
},
|
|
40732
|
+
typescript: {
|
|
40733
|
+
...d.sandbox.typescript,
|
|
40734
|
+
...p["sandbox"]?.["typescript"] ?? {}
|
|
40600
40735
|
}
|
|
40601
40736
|
},
|
|
40602
|
-
llm:
|
|
40737
|
+
llm: mergeLlmSelection(p["llm"]),
|
|
40738
|
+
agentCapabilities: {
|
|
40739
|
+
...sub("agentCapabilities", d.agentCapabilities),
|
|
40740
|
+
uiPresets: readStringList$1(p["agentCapabilities"]?.["uiPresets"], d.agentCapabilities.uiPresets)
|
|
40741
|
+
},
|
|
40742
|
+
security: sub("security", d.security)
|
|
40603
40743
|
};
|
|
40604
40744
|
}
|
|
40745
|
+
function readStringList$1(raw, fallback) {
|
|
40746
|
+
if (!Array.isArray(raw)) return [...fallback];
|
|
40747
|
+
return raw.filter((item) => typeof item === "string");
|
|
40748
|
+
}
|
|
40605
40749
|
/**
|
|
40606
40750
|
* Wraps a store whose load() may return partial/legacy data, normalizing every read
|
|
40607
40751
|
* through mergeRuntimeConfigDefaults so all consumers share one defaults-filling
|
|
@@ -40615,6 +40759,146 @@ function withRuntimeConfigDefaults(store) {
|
|
|
40615
40759
|
};
|
|
40616
40760
|
}
|
|
40617
40761
|
|
|
40762
|
+
//#endregion
|
|
40763
|
+
//#region ../core/src/errors.ts
|
|
40764
|
+
/**
|
|
40765
|
+
* 所有公开 API 抛出的结构化错误,code 供上层可编程处理
|
|
40766
|
+
* @stable
|
|
40767
|
+
*/
|
|
40768
|
+
var WebSkillError$1 = class extends Error {
|
|
40769
|
+
code;
|
|
40770
|
+
details;
|
|
40771
|
+
constructor(code, message, details) {
|
|
40772
|
+
super(message);
|
|
40773
|
+
this.name = "WebSkillError";
|
|
40774
|
+
this.code = code;
|
|
40775
|
+
this.details = details;
|
|
40776
|
+
}
|
|
40777
|
+
};
|
|
40778
|
+
|
|
40779
|
+
//#endregion
|
|
40780
|
+
//#region ../ui-kit/src/runtime-config/preferences.ts
|
|
40781
|
+
/** 导出结构的版本;导入时逐字校验,不做向前兼容猜测 */
|
|
40782
|
+
const PREFERENCE_EXPORT_VERSION = 1;
|
|
40783
|
+
/**
|
|
40784
|
+
* 判定为凭据的键名片段(归一化后按**子串**匹配)。用子串而不是全等,是因为被污染的
|
|
40785
|
+
* 文件不会只带 `apiKey`——`accessToken`、`clientSecret`、`userPassword` 都得拦住。
|
|
40786
|
+
* 偏好结构里的合法键(id/label/provider/model/theme/locale/uiPresets…)不含这些片段。
|
|
40787
|
+
*/
|
|
40788
|
+
const CREDENTIAL_MARKERS = [
|
|
40789
|
+
"apikey",
|
|
40790
|
+
"secret",
|
|
40791
|
+
"token",
|
|
40792
|
+
"password",
|
|
40793
|
+
"passphrase",
|
|
40794
|
+
"credential",
|
|
40795
|
+
"privatekey"
|
|
40796
|
+
];
|
|
40797
|
+
const normalizeKey = (key) => key.toLowerCase().replace(/[-_\s]/g, "");
|
|
40798
|
+
const isCredentialKey = (key) => {
|
|
40799
|
+
const normalized = normalizeKey(key);
|
|
40800
|
+
return CREDENTIAL_MARKERS.some((marker) => normalized.includes(marker));
|
|
40801
|
+
};
|
|
40802
|
+
/** 深度扫描凭据键;命中返回第一个原始键名 */
|
|
40803
|
+
function findCredentialKey(value) {
|
|
40804
|
+
if (Array.isArray(value)) {
|
|
40805
|
+
for (const item of value) {
|
|
40806
|
+
const hit = findCredentialKey(item);
|
|
40807
|
+
if (hit !== void 0) return hit;
|
|
40808
|
+
}
|
|
40809
|
+
return;
|
|
40810
|
+
}
|
|
40811
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
40812
|
+
for (const [key, child] of Object.entries(value)) {
|
|
40813
|
+
if (isCredentialKey(key)) return key;
|
|
40814
|
+
const hit = findCredentialKey(child);
|
|
40815
|
+
if (hit !== void 0) return hit;
|
|
40816
|
+
}
|
|
40817
|
+
}
|
|
40818
|
+
/**
|
|
40819
|
+
* 导出偏好(FR-5.1)。凭据在类型层面就进不来,这里再加一条运行时断言:
|
|
40820
|
+
* 类型可以被 `as` 绕过,而这个文件会被用户发给同事、传网盘(AC-5.1 双保险)。
|
|
40821
|
+
* @experimental
|
|
40822
|
+
*/
|
|
40823
|
+
function exportPreferences(config, options) {
|
|
40824
|
+
const models = config.llm.entries.map((entry) => {
|
|
40825
|
+
const { apiKey: _dropped, ...rest } = entry;
|
|
40826
|
+
return rest;
|
|
40827
|
+
});
|
|
40828
|
+
const result = {
|
|
40829
|
+
version: 1,
|
|
40830
|
+
exportedAt: options?.now?.() ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
40831
|
+
preferences: {
|
|
40832
|
+
...options?.appearance ? { appearance: options.appearance } : {},
|
|
40833
|
+
interaction: {
|
|
40834
|
+
missingParams: config.interaction.missingParams,
|
|
40835
|
+
confirmations: config.interaction.confirmations
|
|
40836
|
+
},
|
|
40837
|
+
models,
|
|
40838
|
+
agentCapabilities: {
|
|
40839
|
+
todo: config.agentCapabilities.todo,
|
|
40840
|
+
skillGeneration: config.agentCapabilities.skillGeneration,
|
|
40841
|
+
generativeUi: config.agentCapabilities.generativeUi,
|
|
40842
|
+
formAutofill: config.agentCapabilities.formAutofill,
|
|
40843
|
+
delegation: config.agentCapabilities.delegation,
|
|
40844
|
+
uiPresets: [...config.agentCapabilities.uiPresets]
|
|
40845
|
+
}
|
|
40846
|
+
}
|
|
40847
|
+
};
|
|
40848
|
+
const leaked = findCredentialKey(result);
|
|
40849
|
+
if (leaked !== void 0) throw new WebSkillError$1("PREFERENCE_IMPORT_CREDENTIAL_REJECTED", `Preference export must not contain credentials, but "${leaked}" was present`);
|
|
40850
|
+
return result;
|
|
40851
|
+
}
|
|
40852
|
+
/**
|
|
40853
|
+
* 解析并校验导入内容(FR-5.3 / FR-5.4)。含凭据是**拒绝**而不是忽略:
|
|
40854
|
+
* 文件里有密钥说明它可能来自被污染的来源,静默丢弃会掩盖问题。
|
|
40855
|
+
* @experimental
|
|
40856
|
+
*/
|
|
40857
|
+
function importPreferences(raw) {
|
|
40858
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new WebSkillError$1("PREFERENCE_IMPORT_INVALID", "Preference file must be a JSON object");
|
|
40859
|
+
const record = raw;
|
|
40860
|
+
if (record["version"] !== 1) throw new WebSkillError$1("PREFERENCE_IMPORT_VERSION_UNSUPPORTED", `Unsupported preference file version ${JSON.stringify(record["version"])}; expected ${1}`);
|
|
40861
|
+
const preferences = record["preferences"];
|
|
40862
|
+
if (typeof preferences !== "object" || preferences === null || Array.isArray(preferences)) throw new WebSkillError$1("PREFERENCE_IMPORT_INVALID", "Preference file is missing a \"preferences\" object");
|
|
40863
|
+
const leaked = findCredentialKey(record);
|
|
40864
|
+
if (leaked !== void 0) throw new WebSkillError$1("PREFERENCE_IMPORT_CREDENTIAL_REJECTED", `Preference file contains a credential field "${leaked}"; import was rejected`);
|
|
40865
|
+
return {
|
|
40866
|
+
version: 1,
|
|
40867
|
+
exportedAt: String(record["exportedAt"] ?? ""),
|
|
40868
|
+
preferences
|
|
40869
|
+
};
|
|
40870
|
+
}
|
|
40871
|
+
/**
|
|
40872
|
+
* 把导入的偏好合到当前配置上(FR-5.4)。模型条目的 `apiKey` 一律留空——
|
|
40873
|
+
* 导出物里本来就没有,用户须在导入后重新填写。
|
|
40874
|
+
* @experimental
|
|
40875
|
+
*/
|
|
40876
|
+
function applyPreferences(config, imported) {
|
|
40877
|
+
const { interaction, models, agentCapabilities } = imported.preferences;
|
|
40878
|
+
const entries = models?.map((entry) => ({ ...entry }));
|
|
40879
|
+
return {
|
|
40880
|
+
...config,
|
|
40881
|
+
interaction: {
|
|
40882
|
+
...config.interaction,
|
|
40883
|
+
...interaction?.missingParams === "user" || interaction?.missingParams === "llm" ? { missingParams: interaction.missingParams } : {},
|
|
40884
|
+
...interaction?.confirmations === "required" || interaction?.confirmations === "auto-approve" ? { confirmations: interaction.confirmations } : {}
|
|
40885
|
+
},
|
|
40886
|
+
...entries ? { llm: {
|
|
40887
|
+
entries,
|
|
40888
|
+
...entries[0] ? { defaultId: entries[0].id } : {}
|
|
40889
|
+
} } : {},
|
|
40890
|
+
agentCapabilities: {
|
|
40891
|
+
...config.agentCapabilities,
|
|
40892
|
+
...typeof agentCapabilities?.todo === "boolean" ? { todo: agentCapabilities.todo } : {},
|
|
40893
|
+
...typeof agentCapabilities?.skillGeneration === "boolean" ? { skillGeneration: agentCapabilities.skillGeneration } : {},
|
|
40894
|
+
...typeof agentCapabilities?.generativeUi === "boolean" ? { generativeUi: agentCapabilities.generativeUi } : {},
|
|
40895
|
+
...typeof agentCapabilities?.formAutofill === "boolean" ? { formAutofill: agentCapabilities.formAutofill } : {},
|
|
40896
|
+
...typeof agentCapabilities?.delegation === "boolean" ? { delegation: agentCapabilities.delegation } : {},
|
|
40897
|
+
...Array.isArray(agentCapabilities?.uiPresets) ? { uiPresets: agentCapabilities.uiPresets.filter((name) => typeof name === "string") } : {}
|
|
40898
|
+
}
|
|
40899
|
+
};
|
|
40900
|
+
}
|
|
40901
|
+
|
|
40618
40902
|
//#endregion
|
|
40619
40903
|
//#region src/core/interactionBridge.ts
|
|
40620
40904
|
/**
|
|
@@ -40817,10 +41101,79 @@ var CompositeUiBridge = class {
|
|
|
40817
41101
|
}
|
|
40818
41102
|
};
|
|
40819
41103
|
|
|
41104
|
+
//#endregion
|
|
41105
|
+
//#region src/core/subAgent.ts
|
|
41106
|
+
/**
|
|
41107
|
+
* 子 agent 不继承的工具来源(设计 03 §20):
|
|
41108
|
+
* - `delegation`:孙 agent 递归委派不在本版范围;
|
|
41109
|
+
* - `todo`:清单归父 agent,子 agent 拿到 `manage_todo` 就能整表替换父级计划。
|
|
41110
|
+
*/
|
|
41111
|
+
const SUB_AGENT_EXCLUDED_KINDS = /* @__PURE__ */ new Set(["delegation", "todo"]);
|
|
41112
|
+
/** 限定子 agent 可用工具名(`allowedTools` 缺省即继承父级全集) */
|
|
41113
|
+
function restrict(source, allowed) {
|
|
41114
|
+
return {
|
|
41115
|
+
...source,
|
|
41116
|
+
listToolSpecs: async () => (await source.listToolSpecs()).filter((spec) => allowed.has(spec.name)),
|
|
41117
|
+
canHandle: (name) => allowed.has(name) && source.canHandle(name)
|
|
41118
|
+
};
|
|
41119
|
+
}
|
|
41120
|
+
/**
|
|
41121
|
+
* chatbot 侧的 `SubAgentRunner`(设计 03 §18 注记里欠的那一半装配)。
|
|
41122
|
+
*
|
|
41123
|
+
* 每次委派新建一个子 `WebSkillRuntime`,而不是复用父实例:
|
|
41124
|
+
* 预算(`maxTurns` / `totalTimeoutMs`)是构造期配置,复用就没法按 `input.budget` 收紧;
|
|
41125
|
+
* 事件总线也必须是独立的,否则子 run 的 `route` 事件会被 chatbot 当成新的一轮对话。
|
|
41126
|
+
*
|
|
41127
|
+
* 上下文隔离不靠约定:子 run 不带任何 history,父 agent 只从返回值里拿到 `summary`。
|
|
41128
|
+
*/
|
|
41129
|
+
function createChatbotSubAgentRunner(options) {
|
|
41130
|
+
const { deps, loopConfig, uiBridge } = options;
|
|
41131
|
+
return async (input) => {
|
|
41132
|
+
const inherited = (deps.externalTools ?? []).filter((source) => !SUB_AGENT_EXCLUDED_KINDS.has(source.kind));
|
|
41133
|
+
const allowed = input.allowedTools ? new Set(input.allowedTools) : void 0;
|
|
41134
|
+
const events = new EventBus();
|
|
41135
|
+
const runtime = new WebSkillRuntime({
|
|
41136
|
+
...deps,
|
|
41137
|
+
eventBus: events,
|
|
41138
|
+
uiBridge: withDelegationOrigin(uiBridge, input.origin),
|
|
41139
|
+
externalTools: allowed ? inherited.map((source) => restrict(source, allowed)) : inherited,
|
|
41140
|
+
config: {
|
|
41141
|
+
...loopConfig,
|
|
41142
|
+
maxTurns: input.budget.maxTurns,
|
|
41143
|
+
totalTimeoutMs: input.budget.timeoutMs
|
|
41144
|
+
}
|
|
41145
|
+
});
|
|
41146
|
+
let subRunId;
|
|
41147
|
+
let abortedBeforeStart = false;
|
|
41148
|
+
events.on("*", (event) => {
|
|
41149
|
+
if (event.phase !== "route") return;
|
|
41150
|
+
subRunId = event.runId;
|
|
41151
|
+
if (abortedBeforeStart) runtime.cancel(event.runId);
|
|
41152
|
+
});
|
|
41153
|
+
const onAbort = () => {
|
|
41154
|
+
if (subRunId === void 0) abortedBeforeStart = true;
|
|
41155
|
+
else runtime.cancel(subRunId);
|
|
41156
|
+
};
|
|
41157
|
+
input.signal.addEventListener("abort", onAbort);
|
|
41158
|
+
try {
|
|
41159
|
+
const result = await runtime.run(input.task);
|
|
41160
|
+
if (result.run.status !== "completed") throw new WebSkillError("RUN_FAILED", result.output);
|
|
41161
|
+
return { summary: result.output };
|
|
41162
|
+
} finally {
|
|
41163
|
+
input.signal.removeEventListener("abort", onAbort);
|
|
41164
|
+
}
|
|
41165
|
+
};
|
|
41166
|
+
}
|
|
41167
|
+
|
|
40820
41168
|
//#endregion
|
|
40821
41169
|
//#region src/core/chatEngine.ts
|
|
40822
41170
|
/** 注入模型的单个附件正文上限(超出截断并标注) */
|
|
40823
41171
|
const ATTACHMENT_TEXT_LIMIT = 32 * 1024;
|
|
41172
|
+
/**
|
|
41173
|
+
* 表单填写值归属的 user 作用域标识。chatbot 是单人本地应用,没有账号体系,
|
|
41174
|
+
* 固定值即可;它只决定 memory 里的作用域名,不参与任何鉴权。
|
|
41175
|
+
*/
|
|
41176
|
+
const CHATBOT_AUTOFILL_USER_ID = "local";
|
|
40824
41177
|
/** sdk 的 `SessionMeta` → chatbot 的列表视图类型(跨层适配,非复刻) */
|
|
40825
41178
|
const toSessionMeta = (session) => ({
|
|
40826
41179
|
id: session.id,
|
|
@@ -40870,32 +41223,47 @@ const SANDBOX_MODE = {
|
|
|
40870
41223
|
"blob-worker": "worker",
|
|
40871
41224
|
"iframe-sandbox": "iframe"
|
|
40872
41225
|
};
|
|
40873
|
-
/**
|
|
40874
|
-
|
|
40875
|
-
|
|
40876
|
-
|
|
40877
|
-
|
|
41226
|
+
/** 条目能否真的发请求:云端要有端点或密钥,浏览器内置两者都不需要 */
|
|
41227
|
+
const isUsableEntry = (entry) => entry.provider === "chrome-builtin" || Boolean(entry.apiKey?.trim()) || Boolean(entry.baseUrl?.trim());
|
|
41228
|
+
/** 选中条目 → 默认条目 → 首条;都没有则返回 undefined(走内置演示) */
|
|
41229
|
+
function pickLlmEntry(rc, selectedId) {
|
|
41230
|
+
const entries = rc?.llm.entries ?? [];
|
|
41231
|
+
const byId = (id) => id === void 0 ? void 0 : entries.find((entry) => entry.id === id);
|
|
41232
|
+
const entry = byId(selectedId) ?? byId(rc?.llm.defaultId) ?? entries[0];
|
|
41233
|
+
return entry !== void 0 && isUsableEntry(entry) ? entry : void 0;
|
|
41234
|
+
}
|
|
41235
|
+
/**
|
|
41236
|
+
* 装配时(而非执行时)把 `sandbox.typescript` 转成执行器参数,让配置错误早暴露。
|
|
41237
|
+
* 只对绝对 URL 走 `RemoteUrlPolicy`:同源相对路径与打包器 specifier 不是出站请求。
|
|
41238
|
+
*/
|
|
41239
|
+
function resolveTypeScriptSupport(config, remoteUrl) {
|
|
41240
|
+
if (!config.enabled) return void 0;
|
|
41241
|
+
const esbuildUrl = config.esbuildUrl?.trim() ?? "";
|
|
41242
|
+
if (esbuildUrl === "") throw new WebSkillError("TS_RESOURCE_URL_REJECTED", "TypeScript support is enabled but no esbuild URL is configured (sandbox.typescript.esbuildUrl)");
|
|
41243
|
+
const wasmUrl = config.wasmUrl?.trim() ?? "";
|
|
41244
|
+
assertTsResourceUrl(esbuildUrl, remoteUrl);
|
|
41245
|
+
if (wasmUrl !== "") assertTsResourceUrl(wasmUrl, remoteUrl);
|
|
41246
|
+
return {
|
|
41247
|
+
esbuildUrl,
|
|
41248
|
+
...wasmUrl !== "" ? { wasmUrl } : {}
|
|
40878
41249
|
};
|
|
40879
|
-
|
|
40880
|
-
|
|
40881
|
-
|
|
40882
|
-
|
|
40883
|
-
|
|
40884
|
-
|
|
40885
|
-
|
|
40886
|
-
|
|
40887
|
-
|
|
40888
|
-
|
|
40889
|
-
|
|
40890
|
-
|
|
40891
|
-
|
|
40892
|
-
model: config.model,
|
|
40893
|
-
...config.requestTimeoutMs !== void 0 ? { requestTimeoutMs: config.requestTimeoutMs } : {}
|
|
40894
|
-
});
|
|
41250
|
+
}
|
|
41251
|
+
function assertTsResourceUrl(value, remoteUrl) {
|
|
41252
|
+
let absolute;
|
|
41253
|
+
try {
|
|
41254
|
+
absolute = new URL(value);
|
|
41255
|
+
} catch {
|
|
41256
|
+
return;
|
|
41257
|
+
}
|
|
41258
|
+
try {
|
|
41259
|
+
assertRemoteUrlAllowed(absolute.href, remoteUrl);
|
|
41260
|
+
} catch (e) {
|
|
41261
|
+
throw new WebSkillError("TS_RESOURCE_URL_REJECTED", `TypeScript transpiler resource is not allowed by the remote URL policy: ${messageOf(e)}`, e);
|
|
41262
|
+
}
|
|
40895
41263
|
}
|
|
40896
41264
|
/**
|
|
40897
41265
|
* 对话框引擎:装配 WebSkillRuntime + 会话持久化 + 事件分发。
|
|
40898
|
-
*
|
|
41266
|
+
* RuntimeConfig 的 load() 是异步的,runtime 在首次 send 前懒装配(#ensureReady)。
|
|
40899
41267
|
* @stable
|
|
40900
41268
|
*/
|
|
40901
41269
|
var ChatEngine = class {
|
|
@@ -40925,6 +41293,27 @@ var ChatEngine = class {
|
|
|
40925
41293
|
#traceStore;
|
|
40926
41294
|
/** 会话落盘;缺省是本包内引用 `FsSessionStore` 的唯一处 */
|
|
40927
41295
|
#sessions;
|
|
41296
|
+
/**
|
|
41297
|
+
* 待办清单容器(需求 3)。引擎级单例:一份清单属于最近开始的 run,
|
|
41298
|
+
* 与「一次只有一个 agent 在跑」的串行模型一致。
|
|
41299
|
+
*/
|
|
41300
|
+
#todoStore = new TodoStore();
|
|
41301
|
+
/** 当前 run('route' 事件捕获,终态清理);清单与页面感知的事件归属共用 */
|
|
41302
|
+
#activeRunId;
|
|
41303
|
+
/**
|
|
41304
|
+
* 会话级模型选择(需求 4):用户在 composer 里选的条目 id。
|
|
41305
|
+
* 属于会话状态而不是配置——切换只影响后续消息,已发出的消息各自记着当时的模型。
|
|
41306
|
+
*/
|
|
41307
|
+
#selectedModelId;
|
|
41308
|
+
/** 最近一次装配实际用上的条目 id(写进 assistant 消息的 `model`) */
|
|
41309
|
+
#activeModelId;
|
|
41310
|
+
/** runId → 清单快照(随 assistant 消息持久化,供历史重放) */
|
|
41311
|
+
#runTodos = /* @__PURE__ */ new Map();
|
|
41312
|
+
/** 本 run 内的页面感知记录(写进 assistant 消息,供历史回看) */
|
|
41313
|
+
#runPerceptions = /* @__PURE__ */ new Map();
|
|
41314
|
+
/** 父 run 的预算消耗(委派时按「父级剩余」收紧子 agent 预算,FR-11.4) */
|
|
41315
|
+
#runStartedMs = 0;
|
|
41316
|
+
#runTurn = 0;
|
|
40928
41317
|
constructor(adapter, options = {}) {
|
|
40929
41318
|
this.#adapter = adapter;
|
|
40930
41319
|
this.#options = options;
|
|
@@ -40954,11 +41343,49 @@ var ChatEngine = class {
|
|
|
40954
41343
|
getInteractionChannel: () => this.#interactionChannel,
|
|
40955
41344
|
emit: (event) => this.#emit(event)
|
|
40956
41345
|
});
|
|
41346
|
+
this.#todoStore.subscribe(() => {
|
|
41347
|
+
const runId = this.#activeRunId;
|
|
41348
|
+
if (runId === void 0) return;
|
|
41349
|
+
const items = this.#todoStore.snapshot().items;
|
|
41350
|
+
if (items.length > 0) this.#runTodos.set(runId, items);
|
|
41351
|
+
this.#emit({
|
|
41352
|
+
type: "todo-changed",
|
|
41353
|
+
runId,
|
|
41354
|
+
items
|
|
41355
|
+
});
|
|
41356
|
+
});
|
|
41357
|
+
this.#adapter.pagePerception?.subscribe((record) => {
|
|
41358
|
+
const runId = this.#activeRunId;
|
|
41359
|
+
if (runId === void 0) return;
|
|
41360
|
+
this.#runPerceptions.set(runId, [...this.#runPerceptions.get(runId) ?? [], record]);
|
|
41361
|
+
this.#emit({
|
|
41362
|
+
type: "page-perceived",
|
|
41363
|
+
runId,
|
|
41364
|
+
record
|
|
41365
|
+
});
|
|
41366
|
+
});
|
|
41367
|
+
}
|
|
41368
|
+
/** 当前待办清单快照(宿主可直读;实时更新订阅 `todo-changed` 事件) */
|
|
41369
|
+
get todos() {
|
|
41370
|
+
return this.#todoStore.snapshot().items;
|
|
40957
41371
|
}
|
|
40958
41372
|
/** 当前渲染框架档 */
|
|
40959
41373
|
get renderer() {
|
|
40960
41374
|
return this.#renderer;
|
|
40961
41375
|
}
|
|
41376
|
+
/** 当前会话选中的模型条目 id;未显式切换过时为 undefined(走 `RuntimeConfig.llm.defaultId`) */
|
|
41377
|
+
get modelId() {
|
|
41378
|
+
return this.#selectedModelId;
|
|
41379
|
+
}
|
|
41380
|
+
/**
|
|
41381
|
+
* 切换本会话后续消息使用的模型条目(需求 4)。传 `undefined` 回到配置里的默认项。
|
|
41382
|
+
* 重装配 runtime 以换掉客户端;已落盘的消息不受影响。
|
|
41383
|
+
*/
|
|
41384
|
+
setModel(entryId) {
|
|
41385
|
+
if (entryId === this.#selectedModelId) return;
|
|
41386
|
+
this.#selectedModelId = entryId;
|
|
41387
|
+
this.reloadConfig();
|
|
41388
|
+
}
|
|
40962
41389
|
/** 渲染框架热切换:复合桥按档分发,无需重装配 runtime(会话句柄保留) */
|
|
40963
41390
|
setRenderer(renderer) {
|
|
40964
41391
|
if (renderer === this.#renderer) return;
|
|
@@ -40990,10 +41417,6 @@ var ChatEngine = class {
|
|
|
40990
41417
|
this.#handles.clear();
|
|
40991
41418
|
this.#maxHistoryMessages = void 0;
|
|
40992
41419
|
}
|
|
40993
|
-
/** 旧名保留(与 reloadConfig 同语义,向后兼容) */
|
|
40994
|
-
reloadLlm() {
|
|
40995
|
-
this.reloadConfig();
|
|
40996
|
-
}
|
|
40997
41420
|
/**
|
|
40998
41421
|
* 会话列表页。归档会话也返回:UI 自己分区展示。
|
|
40999
41422
|
* `limit` 不给默认值——缺省属于 `SessionStore` 实现,在这里兜底就等于只下推了一半。
|
|
@@ -41011,6 +41434,7 @@ var ChatEngine = class {
|
|
|
41011
41434
|
async createSession() {
|
|
41012
41435
|
const session = await this.#sessions.create({ id: newId("session") });
|
|
41013
41436
|
this.#currentSessionId = session.id;
|
|
41437
|
+
this.setModel(void 0);
|
|
41014
41438
|
return {
|
|
41015
41439
|
id: session.id,
|
|
41016
41440
|
createdAt: session.createdAt
|
|
@@ -41023,6 +41447,7 @@ var ChatEngine = class {
|
|
|
41023
41447
|
async selectSession(id, options = {}) {
|
|
41024
41448
|
const page = await this.#sessions.listMessages(id, options);
|
|
41025
41449
|
this.#currentSessionId = id;
|
|
41450
|
+
if (options.cursor === void 0) this.setModel([...page.items].reverse().find((message) => message.model !== void 0)?.model);
|
|
41026
41451
|
return page;
|
|
41027
41452
|
}
|
|
41028
41453
|
/** 向历史方向再取一页;`cursor` 只能来自上一页的 `nextCursor` */
|
|
@@ -41212,29 +41637,32 @@ var ChatEngine = class {
|
|
|
41212
41637
|
return this.#ready;
|
|
41213
41638
|
}
|
|
41214
41639
|
/**
|
|
41215
|
-
* LLM 选择链:options.llm(demo/测试)→ RuntimeConfig.llm
|
|
41216
|
-
* →
|
|
41640
|
+
* LLM 选择链:options.llm(demo/测试)→ `RuntimeConfig.llm` 里选中/默认的条目
|
|
41641
|
+
* → 内置演示。streaming=false 时包装掉 stream(AgentLoop 走 complete)。
|
|
41217
41642
|
*/
|
|
41218
|
-
|
|
41643
|
+
#resolveLlm(rc) {
|
|
41644
|
+
const entry = pickLlmEntry(rc, this.#selectedModelId);
|
|
41219
41645
|
let llm;
|
|
41220
41646
|
let model;
|
|
41221
|
-
|
|
41222
|
-
|
|
41223
|
-
llm =
|
|
41224
|
-
|
|
41225
|
-
} else {
|
|
41226
|
-
|
|
41227
|
-
|
|
41228
|
-
|
|
41229
|
-
}
|
|
41647
|
+
let entryId;
|
|
41648
|
+
if (this.#options.llm) {
|
|
41649
|
+
llm = this.#options.llm;
|
|
41650
|
+
entryId = entry?.id;
|
|
41651
|
+
} else if (entry) {
|
|
41652
|
+
llm = createLlmClient(entry);
|
|
41653
|
+
model = entry.model.trim() === "" ? void 0 : entry.model;
|
|
41654
|
+
entryId = entry.id;
|
|
41655
|
+
} else llm = createDemoLlm();
|
|
41230
41656
|
if (rc && !rc.streaming && llm.stream) llm = { complete: llm.complete.bind(llm) };
|
|
41231
|
-
return
|
|
41657
|
+
return {
|
|
41232
41658
|
llm,
|
|
41233
|
-
model
|
|
41234
|
-
|
|
41659
|
+
...model !== void 0 ? { model } : {},
|
|
41660
|
+
...entryId !== void 0 ? { entryId } : {}
|
|
41661
|
+
};
|
|
41235
41662
|
}
|
|
41236
41663
|
/** 默认沙箱执行器(adapter.executor 未注入时):RuntimeConfig.sandbox/interaction.approvalScope 映射构造参数 */
|
|
41237
41664
|
#createExecutor(fs, rc) {
|
|
41665
|
+
const typescript = rc ? resolveTypeScriptSupport(rc.sandbox.typescript, rc.sandbox.remoteUrl) : void 0;
|
|
41238
41666
|
const deps = {
|
|
41239
41667
|
fs,
|
|
41240
41668
|
...rc ? {
|
|
@@ -41243,7 +41671,8 @@ var ChatEngine = class {
|
|
|
41243
41671
|
capabilities: rc.sandbox.capabilities,
|
|
41244
41672
|
approvalScope: rc.interaction.approvalScope,
|
|
41245
41673
|
uiBridge: this.#uiBridge
|
|
41246
|
-
} : {}
|
|
41674
|
+
} : {},
|
|
41675
|
+
...typescript ? { typescript } : {}
|
|
41247
41676
|
};
|
|
41248
41677
|
return this.#options.executorFactory?.(deps) ?? new BrowserWorkerScriptExecutor(deps);
|
|
41249
41678
|
}
|
|
@@ -41270,11 +41699,37 @@ var ChatEngine = class {
|
|
|
41270
41699
|
...g.catalogFilter !== void 0 ? { catalogFilter: g.catalogFilter } : {}
|
|
41271
41700
|
};
|
|
41272
41701
|
}
|
|
41702
|
+
/**
|
|
41703
|
+
* 清除跨会话表单填写历史(FR-5.11)。不传 `fieldKey` 即整体清空。
|
|
41704
|
+
* 与开关分开:关掉开关只是停止召回,用户往往还想把已存的内容删干净。
|
|
41705
|
+
* @experimental
|
|
41706
|
+
*/
|
|
41707
|
+
async clearFormAutofill(fieldKey) {
|
|
41708
|
+
await clearStoredFormValues(new FsMemoryStore({
|
|
41709
|
+
root: `${this.#chatRoot}/memory`,
|
|
41710
|
+
fs: this.#adapter.storage
|
|
41711
|
+
}), CHATBOT_AUTOFILL_USER_ID, fieldKey);
|
|
41712
|
+
}
|
|
41713
|
+
/**
|
|
41714
|
+
* 父 run 的剩余预算(FR-11.4):子 agent 拿到的额度不得超过它。
|
|
41715
|
+
* 轮次与耗时从生命周期事件推算——`AgentLoop` 不对外暴露内部计数,
|
|
41716
|
+
* 而 `execute`/`turn` 事件本来就是它逐轮发出来的,够用且不需要改 runtime。
|
|
41717
|
+
* 默认值与 `AgentLoop` 保持一致(maxTurns 10 / totalTimeoutMs 120s)。
|
|
41718
|
+
*/
|
|
41719
|
+
#parentBudget(loopConfig) {
|
|
41720
|
+
const elapsed = this.#runStartedMs > 0 ? Date.now() - this.#runStartedMs : 0;
|
|
41721
|
+
return {
|
|
41722
|
+
remainingTurns: Math.max(0, (loopConfig.maxTurns ?? 10) - this.#runTurn),
|
|
41723
|
+
remainingTimeoutMs: Math.max(0, (loopConfig.totalTimeoutMs ?? 12e4) - elapsed)
|
|
41724
|
+
};
|
|
41725
|
+
}
|
|
41273
41726
|
async #assemble() {
|
|
41274
41727
|
const fs = this.#adapter.storage;
|
|
41275
41728
|
const rc = await this.#runtimeConfigStore()?.load();
|
|
41276
|
-
const { llm, model } =
|
|
41729
|
+
const { llm, model, entryId } = this.#resolveLlm(rc);
|
|
41730
|
+
this.#activeModelId = entryId;
|
|
41277
41731
|
const pageSkillSource = this.#adapter.pageSkillSource;
|
|
41732
|
+
const pagePerception = this.#adapter.pagePerception;
|
|
41278
41733
|
const loopConfig = {
|
|
41279
41734
|
...rc ? {
|
|
41280
41735
|
maxTurns: rc.loop.maxTurns,
|
|
@@ -41287,7 +41742,18 @@ var ChatEngine = class {
|
|
|
41287
41742
|
...this.#options.loopConfig
|
|
41288
41743
|
};
|
|
41289
41744
|
this.#maxHistoryMessages = rc?.loop.maxHistoryMessages;
|
|
41290
|
-
const
|
|
41745
|
+
const baseTools = [
|
|
41746
|
+
...pageSkillSource ? [pageSkillSource] : [],
|
|
41747
|
+
...(rc === void 0 ? this.#options.generativeUi === true : rc.agentCapabilities.generativeUi) ? [createUiCatalogToolSource(rc ? { presets: rc.agentCapabilities.uiPresets } : {})] : [],
|
|
41748
|
+
...rc?.agentCapabilities.todo === false ? [] : [createTodoToolSource({ store: this.#todoStore })],
|
|
41749
|
+
...rc?.agentCapabilities.skillGeneration === true ? [createSkillGenerationToolSource({
|
|
41750
|
+
ui: this.#uiBridge,
|
|
41751
|
+
...this.#options.skillCandidates ? { sink: this.#options.skillCandidates } : {},
|
|
41752
|
+
sessionId: () => this.#currentSessionId
|
|
41753
|
+
})] : [],
|
|
41754
|
+
...pagePerception ? [createPagePerceptionToolSource({ policy: pagePerception })] : []
|
|
41755
|
+
];
|
|
41756
|
+
const deps = {
|
|
41291
41757
|
fs,
|
|
41292
41758
|
roots: this.#adapter.skillRoots,
|
|
41293
41759
|
llm,
|
|
@@ -41308,21 +41774,39 @@ var ChatEngine = class {
|
|
|
41308
41774
|
root: `${this.#chatRoot}/memory`,
|
|
41309
41775
|
fs
|
|
41310
41776
|
}),
|
|
41777
|
+
...rc?.agentCapabilities.formAutofill === true ? { formAutofill: { userId: CHATBOT_AUTOFILL_USER_ID } } : {},
|
|
41311
41778
|
...this.#options.hooks ? { hooks: this.#applyHooksConfig(this.#options.hooks, rc) } : {},
|
|
41312
|
-
externalTools:
|
|
41779
|
+
externalTools: baseTools,
|
|
41313
41780
|
skillProviders: pageSkillSource ? [pageSkillSource] : [],
|
|
41314
41781
|
...this.#governanceDeps(),
|
|
41315
41782
|
...Object.keys(loopConfig).length > 0 ? { config: loopConfig } : {},
|
|
41316
41783
|
...model ? { model } : {}
|
|
41784
|
+
};
|
|
41785
|
+
const runtime = new WebSkillRuntime({
|
|
41786
|
+
...deps,
|
|
41787
|
+
externalTools: [...baseTools, ...rc?.agentCapabilities.delegation === true ? [createDelegationToolSource({
|
|
41788
|
+
runner: createChatbotSubAgentRunner({
|
|
41789
|
+
deps,
|
|
41790
|
+
loopConfig,
|
|
41791
|
+
uiBridge: this.#uiBridge
|
|
41792
|
+
}),
|
|
41793
|
+
parentBudget: () => this.#parentBudget(loopConfig),
|
|
41794
|
+
todos: this.#todoStore
|
|
41795
|
+
})] : []]
|
|
41317
41796
|
});
|
|
41318
41797
|
runtime.events.on("*", (event) => {
|
|
41319
41798
|
if (event.phase === "route") {
|
|
41320
41799
|
this.#runSkills.set(event.runId, /* @__PURE__ */ new Set());
|
|
41800
|
+
this.#activeRunId = event.runId;
|
|
41801
|
+
this.#runStartedMs = Date.parse(event.ts);
|
|
41802
|
+
this.#runTurn = 0;
|
|
41803
|
+
this.#todoStore.clear();
|
|
41321
41804
|
this.#emit({
|
|
41322
41805
|
type: "run-started",
|
|
41323
41806
|
runId: event.runId
|
|
41324
41807
|
});
|
|
41325
41808
|
}
|
|
41809
|
+
if (event.phase === "execute" && event.data.kind === "turn") this.#runTurn = event.data.turn;
|
|
41326
41810
|
const toolData = event.phase === "execute" && event.data.kind === "tool" ? event.data : void 0;
|
|
41327
41811
|
if (!toolData) this.#emit({
|
|
41328
41812
|
type: "lifecycle",
|
|
@@ -41355,7 +41839,8 @@ var ChatEngine = class {
|
|
|
41355
41839
|
runId: event.runId,
|
|
41356
41840
|
callId,
|
|
41357
41841
|
name,
|
|
41358
|
-
...args
|
|
41842
|
+
...args,
|
|
41843
|
+
...toolData.errorCode !== void 0 ? { errorCode: toolData.errorCode } : {}
|
|
41359
41844
|
});
|
|
41360
41845
|
} else if (event.phase === "complete" || event.phase === "fail") this.#runSkills.delete(event.runId);
|
|
41361
41846
|
});
|
|
@@ -41370,6 +41855,8 @@ var ChatEngine = class {
|
|
|
41370
41855
|
const latest = this.bridge.latestResult;
|
|
41371
41856
|
const blocks = latest && latest.runId === run.id ? latest.blocks.filter((block) => block.type !== "markdown" || block.text.trim() !== result.output.trim()) : void 0;
|
|
41372
41857
|
const surfaces = this.bridge.surfaceSnapshots.filter((snapshot) => snapshot.runId === run.id);
|
|
41858
|
+
const todos = this.#runTodos.get(run.id);
|
|
41859
|
+
const perceptions = this.#runPerceptions.get(run.id);
|
|
41373
41860
|
const assistantMessage = {
|
|
41374
41861
|
id: newId("msg"),
|
|
41375
41862
|
role: "assistant",
|
|
@@ -41379,7 +41866,10 @@ var ChatEngine = class {
|
|
|
41379
41866
|
activeSkillNames: run.activeSkillNames,
|
|
41380
41867
|
...blocks && blocks.length > 0 ? { blocks } : {},
|
|
41381
41868
|
...surfaces.length > 0 ? { surfaces } : {},
|
|
41382
|
-
...
|
|
41869
|
+
...todos && todos.length > 0 ? { todos } : {},
|
|
41870
|
+
...perceptions && perceptions.length > 0 ? { perceptions } : {},
|
|
41871
|
+
...toolCalls.length > 0 ? { toolCalls } : {},
|
|
41872
|
+
...this.#activeModelId !== void 0 ? { model: this.#activeModelId } : {}
|
|
41383
41873
|
};
|
|
41384
41874
|
await this.#sessions.appendMessages(sessionId, [assistantMessage]);
|
|
41385
41875
|
await this.#traceStore.put(run);
|
|
@@ -41395,6 +41885,8 @@ var ChatEngine = class {
|
|
|
41395
41885
|
message: assistantMessage
|
|
41396
41886
|
});
|
|
41397
41887
|
this.#runSkills.delete(run.id);
|
|
41888
|
+
this.#runTodos.delete(run.id);
|
|
41889
|
+
this.#runPerceptions.delete(run.id);
|
|
41398
41890
|
}
|
|
41399
41891
|
#snapshotStore() {
|
|
41400
41892
|
return new FsRunSnapshotStore({
|
|
@@ -41481,8 +41973,11 @@ const chatbotDictionary = defineDictionary({
|
|
|
41481
41973
|
"tool.status.running": "Running",
|
|
41482
41974
|
"tool.status.completed": "Completed",
|
|
41483
41975
|
"tool.status.failed": "Failed",
|
|
41976
|
+
"tool.status.denied": "Blocked by policy",
|
|
41484
41977
|
"tool.duration": "{ms} ms",
|
|
41485
41978
|
"tool.args": "Arguments",
|
|
41979
|
+
"tool.denied.reason": "Why it was blocked",
|
|
41980
|
+
"tool.failed.reason": "Why it failed",
|
|
41486
41981
|
"cot.thinking": "Thinking…",
|
|
41487
41982
|
"cot.thought": "Thought for {duration}",
|
|
41488
41983
|
"cot.summary": "Run details",
|
|
@@ -41490,12 +41985,20 @@ const chatbotDictionary = defineDictionary({
|
|
|
41490
41985
|
"cot.working": "Working…",
|
|
41491
41986
|
"trace.title": "Run steps",
|
|
41492
41987
|
"trace.skill": "Skill activated: {name}",
|
|
41988
|
+
"perception.reading": "Reading page content (scope: {scope})",
|
|
41989
|
+
"perception.excluded": "Excluded: {scope}",
|
|
41493
41990
|
"trace.phase.route": "Route",
|
|
41494
41991
|
"trace.phase.activate": "Activate",
|
|
41495
41992
|
"trace.phase.execute": "Execute",
|
|
41496
41993
|
"trace.phase.interact": "Interact",
|
|
41497
41994
|
"trace.phase.complete": "Complete",
|
|
41498
41995
|
"trace.phase.fail": "Failed",
|
|
41996
|
+
"todo.title": "Task list",
|
|
41997
|
+
"todo.progress": "{done}/{total}",
|
|
41998
|
+
"todo.status.pending": "Pending",
|
|
41999
|
+
"todo.status.inProgress": "In progress",
|
|
42000
|
+
"todo.status.completed": "Completed",
|
|
42001
|
+
"todo.delegatedTo": "Delegated to {skill}",
|
|
41499
42002
|
"interaction.ask.title": "Answer needed",
|
|
41500
42003
|
"interaction.confirm.title": "Confirmation",
|
|
41501
42004
|
"interaction.form.title": "Information needed",
|
|
@@ -41504,6 +42007,7 @@ const chatbotDictionary = defineDictionary({
|
|
|
41504
42007
|
"interaction.authorize.description": "This skill requests a capability. Review what it wants to do, then allow or deny the request. Denying cancels the run step that asked.",
|
|
41505
42008
|
"interaction.authorize.capability": "Requested capability",
|
|
41506
42009
|
"interaction.authorize.details": "Request details",
|
|
42010
|
+
"interaction.origin": "From: {label}",
|
|
41507
42011
|
"interaction.submit": "Submit",
|
|
41508
42012
|
"interaction.cancel": "Cancel",
|
|
41509
42013
|
"interaction.confirm": "Confirm",
|
|
@@ -41531,14 +42035,37 @@ const chatbotDictionary = defineDictionary({
|
|
|
41531
42035
|
"composer.attachments": "Attachments",
|
|
41532
42036
|
"composer.addAttachment": "Attach a text file",
|
|
41533
42037
|
"composer.removeAttachment": "Remove attachment: {name}",
|
|
42038
|
+
"composer.model": "Model",
|
|
42039
|
+
"composer.model.demo": "Demo model",
|
|
42040
|
+
"composer.dictate": "Voice input",
|
|
42041
|
+
"composer.dictate.stop": "Stop voice input",
|
|
42042
|
+
"composer.dictate.recording": "Recording",
|
|
42043
|
+
"composer.dictate.api-missing": "This browser does not support speech recognition.",
|
|
42044
|
+
"composer.dictate.insecure-context": "Voice input needs a secure connection (https or localhost).",
|
|
41534
42045
|
"settings.title": "Settings",
|
|
41535
|
-
"settings.model.title": "
|
|
41536
|
-
"settings.model.description": "
|
|
42046
|
+
"settings.model.title": "Models",
|
|
42047
|
+
"settings.model.description": "Configure one or more models and pick a default. The composer lets you switch models per conversation. When no model is configured, a built-in demo model answers so you can explore the interface — its replies are mock text, clearly marked as Demo model.",
|
|
42048
|
+
"settings.model.empty": "No model configured yet.",
|
|
42049
|
+
"settings.model.add": "Add model",
|
|
42050
|
+
"settings.model.edit": "Edit",
|
|
42051
|
+
"settings.model.remove": "Remove",
|
|
42052
|
+
"settings.model.setDefault": "Use as default",
|
|
42053
|
+
"settings.model.default": "Default",
|
|
42054
|
+
"settings.model.label": "Display name",
|
|
42055
|
+
"settings.model.labelNote": "Shown in the composer selector. Leave empty to fall back to the model name.",
|
|
42056
|
+
"settings.model.unavailable": "Unavailable",
|
|
42057
|
+
"settings.model.cancel": "Cancel",
|
|
41537
42058
|
"settings.provider": "Provider",
|
|
41538
42059
|
"settings.providerNote": "Choose the model protocol. Anthropic and Google use their official endpoints; only OpenAI-compatible endpoints need a custom URL.",
|
|
41539
42060
|
"settings.provider.openai-compatible": "OpenAI-compatible",
|
|
41540
42061
|
"settings.provider.anthropic": "Anthropic",
|
|
41541
42062
|
"settings.provider.google": "Google",
|
|
42063
|
+
"settings.provider.chrome-builtin": "Chrome built-in (experimental)",
|
|
42064
|
+
"settings.provider.chrome-builtin.note": "Runs entirely on this device through the browser Prompt API. No endpoint or API key is needed.",
|
|
42065
|
+
"settings.provider.chrome-builtin.limitation": "The built-in model cannot call tools, so skills that run scripts will not work with it.",
|
|
42066
|
+
"settings.chromeBuiltin.api-missing": "This Chrome build does not expose the Prompt API. Update Chrome, or enable the built-in AI flags.",
|
|
42067
|
+
"settings.chromeBuiltin.model-not-ready": "The on-device model is not ready yet. Chrome has to finish downloading it first.",
|
|
42068
|
+
"settings.chromeBuiltin.unsupported-browser": "The Prompt API is only available in Chromium-based browsers.",
|
|
41542
42069
|
"settings.provider.anthropic.note": "The SDK calls the official Anthropic endpoint (api.anthropic.com); only an API key and model are required.",
|
|
41543
42070
|
"settings.provider.google.note": "The SDK calls the official Google endpoint (generativelanguage.googleapis.com); only an API key and model are required.",
|
|
41544
42071
|
"settings.endpoint": "Endpoint",
|
|
@@ -41558,6 +42085,30 @@ const chatbotDictionary = defineDictionary({
|
|
|
41558
42085
|
"settings.demoNote": "No endpoint configured. Replies come from the built-in mock model — connect an endpoint below to chat for real.",
|
|
41559
42086
|
"settings.save": "Save",
|
|
41560
42087
|
"settings.clear": "Clear",
|
|
42088
|
+
"settings.agent.title": "Agent capabilities",
|
|
42089
|
+
"settings.agent.description": "Turn individual agent behaviours on or off. Changes apply immediately to the next run.",
|
|
42090
|
+
"settings.agent.todo": "Task list",
|
|
42091
|
+
"settings.agent.todoNote": "Lets the model break a multi-step request into a checklist and report progress as it works.",
|
|
42092
|
+
"settings.agent.generativeUi": "Generative UI",
|
|
42093
|
+
"settings.agent.generativeUiNote": "Lets the model design forms, tables and charts instead of answering in plain text. Off by default: the component catalogue adds roughly 14 KB to every request.",
|
|
42094
|
+
"settings.agent.skillGeneration": "Skill generation",
|
|
42095
|
+
"settings.agent.skillGenerationNote": "Lets the model turn a procedure from this conversation into a skill candidate. You confirm every candidate, and a reviewer has to approve it before anyone can use it.",
|
|
42096
|
+
"settings.agent.delegation": "Delegate sub-tasks",
|
|
42097
|
+
"settings.agent.delegationNote": "Lets the agent hand a sub-task to a separate run with its own budget, then continue with the summary it returns. Off by default: one request can turn into several model runs.",
|
|
42098
|
+
"settings.agent.formAutofill": "Remember form entries",
|
|
42099
|
+
"settings.agent.formAutofillNote": "Remembers what you last typed into a skill form and offers it as a suggestion next time. Nothing is filled in for you — you decide whether to use it. Off by default.",
|
|
42100
|
+
"settings.agent.formAutofillClear": "Clear history",
|
|
42101
|
+
"settings.agent.formAutofillCleared": "History cleared",
|
|
42102
|
+
"settings.preferences.title": "Preferences",
|
|
42103
|
+
"settings.preferences.description": "Move your settings to another device. API keys are never included — you will re-enter them after importing.",
|
|
42104
|
+
"settings.preferences.export": "Export",
|
|
42105
|
+
"settings.preferences.import": "Import preference file",
|
|
42106
|
+
"settings.preferences.exported": "Preferences exported without credentials.",
|
|
42107
|
+
"settings.preferences.imported": "Preferences imported. Re-enter your API keys to finish.",
|
|
42108
|
+
"settings.voice.title": "Voice input",
|
|
42109
|
+
"settings.voice.description": "Recognition runs in the browser and only starts when you press the microphone. The transcript lands in the message box so you can edit it before sending.",
|
|
42110
|
+
"settings.voice.lang": "Recognition language",
|
|
42111
|
+
"settings.voice.langFollow": "Follow interface language",
|
|
41561
42112
|
"settings.appearance.title": "Appearance",
|
|
41562
42113
|
"settings.appearance.description": "Switch theme and language. The host app is notified of every change so it can persist your preference.",
|
|
41563
42114
|
"settings.theme": "Theme",
|
|
@@ -41623,8 +42174,11 @@ const chatbotDictionary = defineDictionary({
|
|
|
41623
42174
|
"tool.status.running": "运行中",
|
|
41624
42175
|
"tool.status.completed": "已完成",
|
|
41625
42176
|
"tool.status.failed": "失败",
|
|
42177
|
+
"tool.status.denied": "被策略拒绝",
|
|
41626
42178
|
"tool.duration": "{ms} ms",
|
|
41627
42179
|
"tool.args": "参数",
|
|
42180
|
+
"tool.denied.reason": "拒绝原因",
|
|
42181
|
+
"tool.failed.reason": "失败原因",
|
|
41628
42182
|
"cot.thinking": "思考中…",
|
|
41629
42183
|
"cot.thought": "思考耗时 {duration}",
|
|
41630
42184
|
"cot.summary": "运行细节",
|
|
@@ -41632,12 +42186,20 @@ const chatbotDictionary = defineDictionary({
|
|
|
41632
42186
|
"cot.working": "执行中…",
|
|
41633
42187
|
"trace.title": "运行步进",
|
|
41634
42188
|
"trace.skill": "已激活技能:{name}",
|
|
42189
|
+
"perception.reading": "正在读取页面内容(区域:{scope})",
|
|
42190
|
+
"perception.excluded": "已排除:{scope}",
|
|
41635
42191
|
"trace.phase.route": "路由",
|
|
41636
42192
|
"trace.phase.activate": "激活",
|
|
41637
42193
|
"trace.phase.execute": "执行",
|
|
41638
42194
|
"trace.phase.interact": "交互",
|
|
41639
42195
|
"trace.phase.complete": "完成",
|
|
41640
42196
|
"trace.phase.fail": "失败",
|
|
42197
|
+
"todo.title": "任务清单",
|
|
42198
|
+
"todo.progress": "{done}/{total}",
|
|
42199
|
+
"todo.status.pending": "待开始",
|
|
42200
|
+
"todo.status.inProgress": "进行中",
|
|
42201
|
+
"todo.status.completed": "已完成",
|
|
42202
|
+
"todo.delegatedTo": "已委派给 {skill}",
|
|
41641
42203
|
"interaction.ask.title": "需要回答",
|
|
41642
42204
|
"interaction.confirm.title": "请确认",
|
|
41643
42205
|
"interaction.form.title": "需要补充信息",
|
|
@@ -41646,6 +42208,7 @@ const chatbotDictionary = defineDictionary({
|
|
|
41646
42208
|
"interaction.authorize.description": "该技能正在请求一项能力授权。请确认它要做什么,然后允许或拒绝。拒绝会取消发起请求的这一步运行。",
|
|
41647
42209
|
"interaction.authorize.capability": "请求的能力",
|
|
41648
42210
|
"interaction.authorize.details": "请求详情",
|
|
42211
|
+
"interaction.origin": "来自:{label}",
|
|
41649
42212
|
"interaction.submit": "提交",
|
|
41650
42213
|
"interaction.cancel": "取消",
|
|
41651
42214
|
"interaction.confirm": "确认",
|
|
@@ -41673,14 +42236,37 @@ const chatbotDictionary = defineDictionary({
|
|
|
41673
42236
|
"composer.attachments": "附件",
|
|
41674
42237
|
"composer.addAttachment": "添加文本附件",
|
|
41675
42238
|
"composer.removeAttachment": "移除附件:{name}",
|
|
42239
|
+
"composer.model": "模型",
|
|
42240
|
+
"composer.model.demo": "演示模型",
|
|
42241
|
+
"composer.dictate": "语音输入",
|
|
42242
|
+
"composer.dictate.stop": "停止语音输入",
|
|
42243
|
+
"composer.dictate.recording": "录音中",
|
|
42244
|
+
"composer.dictate.api-missing": "当前浏览器不支持语音识别。",
|
|
42245
|
+
"composer.dictate.insecure-context": "语音输入需要安全连接(https 或 localhost)。",
|
|
41676
42246
|
"settings.title": "设置",
|
|
41677
42247
|
"settings.model.title": "模型",
|
|
41678
|
-
"settings.model.description": "
|
|
42248
|
+
"settings.model.description": "可以配置多个模型并指定默认项,在输入区随时切换。未配置任何模型时由内置演示模型应答,回复为 Mock 文本,并标注为 Demo model。",
|
|
42249
|
+
"settings.model.empty": "还没有配置任何模型。",
|
|
42250
|
+
"settings.model.add": "添加模型",
|
|
42251
|
+
"settings.model.edit": "编辑",
|
|
42252
|
+
"settings.model.remove": "删除",
|
|
42253
|
+
"settings.model.setDefault": "设为默认",
|
|
42254
|
+
"settings.model.default": "默认",
|
|
42255
|
+
"settings.model.label": "显示名称",
|
|
42256
|
+
"settings.model.labelNote": "展示在输入区的选择器里。留空则使用模型名。",
|
|
42257
|
+
"settings.model.unavailable": "不可用",
|
|
42258
|
+
"settings.model.cancel": "取消",
|
|
41679
42259
|
"settings.provider": "供应商",
|
|
41680
42260
|
"settings.providerNote": "选择模型协议。Anthropic 与 Google 使用官方端点;仅 OpenAI 兼容协议需要自定义端点 URL。",
|
|
41681
42261
|
"settings.provider.openai-compatible": "OpenAI 兼容",
|
|
41682
42262
|
"settings.provider.anthropic": "Anthropic",
|
|
41683
42263
|
"settings.provider.google": "Google",
|
|
42264
|
+
"settings.provider.chrome-builtin": "Chrome 内置(实验)",
|
|
42265
|
+
"settings.provider.chrome-builtin.note": "经浏览器 Prompt API 完全在本机运行,不需要端点与 API Key。",
|
|
42266
|
+
"settings.provider.chrome-builtin.limitation": "内置模型不支持工具调用,需要执行脚本的技能在这一档跑不起来。",
|
|
42267
|
+
"settings.chromeBuiltin.api-missing": "当前 Chrome 未暴露 Prompt API。请升级 Chrome,或开启内置 AI 相关开关。",
|
|
42268
|
+
"settings.chromeBuiltin.model-not-ready": "本机模型尚未就绪,Chrome 需要先完成下载。",
|
|
42269
|
+
"settings.chromeBuiltin.unsupported-browser": "Prompt API 仅在 Chromium 内核的浏览器上可用。",
|
|
41684
42270
|
"settings.provider.anthropic.note": "SDK 默认调用 Anthropic 官方端点(api.anthropic.com);只需 API Key 与模型。",
|
|
41685
42271
|
"settings.provider.google.note": "SDK 默认调用 Google 官方端点(generativelanguage.googleapis.com);只需 API Key 与模型。",
|
|
41686
42272
|
"settings.endpoint": "端点",
|
|
@@ -41700,6 +42286,30 @@ const chatbotDictionary = defineDictionary({
|
|
|
41700
42286
|
"settings.demoNote": "未配置端点,回复来自内置 Mock 模型——在下方连接端点即可真实对话。",
|
|
41701
42287
|
"settings.save": "保存",
|
|
41702
42288
|
"settings.clear": "清除",
|
|
42289
|
+
"settings.agent.title": "Agent 能力",
|
|
42290
|
+
"settings.agent.description": "逐项开关 Agent 行为,修改在下一次对话立即生效。",
|
|
42291
|
+
"settings.agent.todo": "待办清单",
|
|
42292
|
+
"settings.agent.todoNote": "允许模型把多步骤任务拆成清单,并在执行过程中回报进度。",
|
|
42293
|
+
"settings.agent.generativeUi": "生成式 UI",
|
|
42294
|
+
"settings.agent.generativeUiNote": "允许模型直接设计表单、表格与图表,而不是只用纯文本回答。默认关闭:组件目录会给每次请求增加约 14 KB 开销。",
|
|
42295
|
+
"settings.agent.skillGeneration": "技能自动生成",
|
|
42296
|
+
"settings.agent.skillGenerationNote": "允许模型把本次对话里的做法整理成技能候选。每个候选都需你当场确认,且经审核通过后才能被使用。",
|
|
42297
|
+
"settings.agent.delegation": "委派子任务",
|
|
42298
|
+
"settings.agent.delegationNote": "允许 Agent 把子任务交给一次独立的运行(有自己的预算),拿到结论后再继续。默认关闭:一句话可能展开成多次模型运行。",
|
|
42299
|
+
"settings.agent.formAutofill": "记住表单填写内容",
|
|
42300
|
+
"settings.agent.formAutofillNote": "记住你上次在技能表单里填的内容,下次以建议形式给出。不会替你填进去——用不用由你决定。默认关闭。",
|
|
42301
|
+
"settings.agent.formAutofillClear": "清除历史",
|
|
42302
|
+
"settings.agent.formAutofillCleared": "已清除",
|
|
42303
|
+
"settings.preferences.title": "偏好迁移",
|
|
42304
|
+
"settings.preferences.description": "把设置搬到另一台设备。导出物永远不含 API Key——导入后需要重新填写。",
|
|
42305
|
+
"settings.preferences.export": "导出",
|
|
42306
|
+
"settings.preferences.import": "导入偏好文件",
|
|
42307
|
+
"settings.preferences.exported": "已导出偏好,其中不含任何凭据。",
|
|
42308
|
+
"settings.preferences.imported": "偏好已导入。请重新填写 API Key 后使用。",
|
|
42309
|
+
"settings.voice.title": "语音输入",
|
|
42310
|
+
"settings.voice.description": "识别在浏览器本地进行,只有你按下麦克风才会开始。转写结果落在输入框里,发送前可以自己改。",
|
|
42311
|
+
"settings.voice.lang": "识别语言",
|
|
42312
|
+
"settings.voice.langFollow": "跟随界面语言",
|
|
41703
42313
|
"settings.appearance.title": "外观",
|
|
41704
42314
|
"settings.appearance.description": "切换主题与语言。每次变更都会通知宿主应用,以便持久化你的偏好。",
|
|
41705
42315
|
"settings.theme": "主题",
|
|
@@ -42013,6 +42623,8 @@ const liveMessageId = (runId) => `live-${runId}`;
|
|
|
42013
42623
|
/** WebSkill 专有信息走协议内的 data part 通道 */
|
|
42014
42624
|
const LIFECYCLE_PART = "webskill.lifecycle";
|
|
42015
42625
|
const SKILL_PART = "webskill.skill";
|
|
42626
|
+
/** 页面只读感知提示(需求 10 FR-10.5):携带实际读取范围 */
|
|
42627
|
+
const PERCEPTION_PART = "webskill.perception";
|
|
42016
42628
|
function parseArgs(args) {
|
|
42017
42629
|
if (args === void 0 || args === "") return {};
|
|
42018
42630
|
try {
|
|
@@ -42041,6 +42653,29 @@ function skillPart(name) {
|
|
|
42041
42653
|
data: { name }
|
|
42042
42654
|
};
|
|
42043
42655
|
}
|
|
42656
|
+
function perceptionPart(record) {
|
|
42657
|
+
return {
|
|
42658
|
+
type: "data",
|
|
42659
|
+
name: PERCEPTION_PART,
|
|
42660
|
+
data: {
|
|
42661
|
+
at: record.at,
|
|
42662
|
+
include: [...record.include],
|
|
42663
|
+
...record.exclude && record.exclude.length > 0 ? { exclude: [...record.exclude] } : {},
|
|
42664
|
+
nodeCount: record.nodeCount
|
|
42665
|
+
}
|
|
42666
|
+
};
|
|
42667
|
+
}
|
|
42668
|
+
/**
|
|
42669
|
+
* 失败工具的结构化原因(G7):走 part 的 `result` 槽,工具行据此把
|
|
42670
|
+
* 「被策略拒绝」与「执行出错」分开展示,而不是拿文案去猜。
|
|
42671
|
+
*/
|
|
42672
|
+
function toolErrorResult(call) {
|
|
42673
|
+
if (call.status !== "failed" || call.errorCode === void 0) return {};
|
|
42674
|
+
return { result: {
|
|
42675
|
+
errorCode: call.errorCode,
|
|
42676
|
+
...call.errorMessage !== void 0 ? { errorMessage: call.errorMessage } : {}
|
|
42677
|
+
} };
|
|
42678
|
+
}
|
|
42044
42679
|
function persistedToolPart(call) {
|
|
42045
42680
|
return {
|
|
42046
42681
|
type: "tool-call",
|
|
@@ -42048,7 +42683,8 @@ function persistedToolPart(call) {
|
|
|
42048
42683
|
toolName: call.name,
|
|
42049
42684
|
argsText: call.args ?? "",
|
|
42050
42685
|
args: parseArgs(call.args),
|
|
42051
|
-
isError: call.status === "failed"
|
|
42686
|
+
isError: call.status === "failed",
|
|
42687
|
+
...toolErrorResult(call)
|
|
42052
42688
|
};
|
|
42053
42689
|
}
|
|
42054
42690
|
function liveToolPart(call) {
|
|
@@ -42059,6 +42695,7 @@ function liveToolPart(call) {
|
|
|
42059
42695
|
argsText: call.args ?? "",
|
|
42060
42696
|
args: parseArgs(call.args),
|
|
42061
42697
|
isError: call.status === "failed",
|
|
42698
|
+
...toolErrorResult(call),
|
|
42062
42699
|
timing: {
|
|
42063
42700
|
startedAt: call.startedAt,
|
|
42064
42701
|
...call.endedAt !== void 0 ? { completedAt: call.endedAt } : {}
|
|
@@ -42088,6 +42725,7 @@ function userMessage(message) {
|
|
|
42088
42725
|
function assistantMessage(message) {
|
|
42089
42726
|
const content = [
|
|
42090
42727
|
...(message.activeSkillNames ?? []).map(skillPart),
|
|
42728
|
+
...(message.perceptions ?? []).map(perceptionPart),
|
|
42091
42729
|
...(message.toolCalls ?? []).map(persistedToolPart),
|
|
42092
42730
|
{
|
|
42093
42731
|
type: "text",
|
|
@@ -42112,6 +42750,7 @@ function assistantMessage(message) {
|
|
|
42112
42750
|
...message.runId !== void 0 ? { runId: message.runId } : {},
|
|
42113
42751
|
...message.blocks ? { blocks: message.blocks } : {},
|
|
42114
42752
|
...message.surfaces ? { surfaces: message.surfaces } : {},
|
|
42753
|
+
...message.todos ? { todos: message.todos } : {},
|
|
42115
42754
|
...message.toolCalls ? { toolCalls: message.toolCalls } : {}
|
|
42116
42755
|
}
|
|
42117
42756
|
}
|
|
@@ -42129,6 +42768,7 @@ function liveMessage(live) {
|
|
|
42129
42768
|
const content = [
|
|
42130
42769
|
...live.phases.map(lifecyclePart),
|
|
42131
42770
|
...live.skills.map(skillPart),
|
|
42771
|
+
...live.perceptions.map(perceptionPart),
|
|
42132
42772
|
...live.tools.map(liveToolPart),
|
|
42133
42773
|
{
|
|
42134
42774
|
type: "text",
|
|
@@ -42147,7 +42787,10 @@ function liveMessage(live) {
|
|
|
42147
42787
|
unstable_data: [],
|
|
42148
42788
|
steps: [],
|
|
42149
42789
|
timing: liveTiming(live),
|
|
42150
|
-
custom: {
|
|
42790
|
+
custom: {
|
|
42791
|
+
runId: live.runId,
|
|
42792
|
+
...live.todos.length > 0 ? { todos: live.todos } : {}
|
|
42793
|
+
}
|
|
42151
42794
|
}
|
|
42152
42795
|
};
|
|
42153
42796
|
}
|
|
@@ -42221,8 +42864,78 @@ function useWebSkillAui({ messages, live, sessions, currentSessionId, sessionsLo
|
|
|
42221
42864
|
}) });
|
|
42222
42865
|
}
|
|
42223
42866
|
|
|
42867
|
+
//#endregion
|
|
42868
|
+
//#region src/react/useComposerDictation.ts
|
|
42869
|
+
/** 转写片段拼到已有草稿后面:语音只做追加,不覆盖用户已经打好的字 */
|
|
42870
|
+
function joinText(base, addition) {
|
|
42871
|
+
const tail = addition.trim();
|
|
42872
|
+
if (tail === "") return base;
|
|
42873
|
+
return base === "" ? tail : `${base.trimEnd()} ${tail}`;
|
|
42874
|
+
}
|
|
42875
|
+
/**
|
|
42876
|
+
* composer 的语音输入接线(需求 7)。转写结果只写进输入框,**不发送**(FR-7.8)——
|
|
42877
|
+
* 语音识别不准是常态,自动发送会把识别错误变成不可撤销的操作。
|
|
42878
|
+
*/
|
|
42879
|
+
function useComposerDictation(lang) {
|
|
42880
|
+
const aui = useAui();
|
|
42881
|
+
const [availability] = useState(() => checkDictationAvailability());
|
|
42882
|
+
const [recording, setRecording] = useState(false);
|
|
42883
|
+
const [error, setError] = useState();
|
|
42884
|
+
const sessionRef = useRef(void 0);
|
|
42885
|
+
/** 本次录音开始时输入框里的内容;中间结果每次都在它后面重画,否则会自我叠加 */
|
|
42886
|
+
const baseRef = useRef("");
|
|
42887
|
+
const stop = useCallback(() => {
|
|
42888
|
+
sessionRef.current?.stop();
|
|
42889
|
+
sessionRef.current = void 0;
|
|
42890
|
+
setRecording(false);
|
|
42891
|
+
}, []);
|
|
42892
|
+
useEffect(() => () => sessionRef.current?.stop(), []);
|
|
42893
|
+
return {
|
|
42894
|
+
availability,
|
|
42895
|
+
recording,
|
|
42896
|
+
toggle: useCallback(() => {
|
|
42897
|
+
if (sessionRef.current) {
|
|
42898
|
+
stop();
|
|
42899
|
+
return;
|
|
42900
|
+
}
|
|
42901
|
+
if (!availability.available) return;
|
|
42902
|
+
const composer = aui.thread.composer();
|
|
42903
|
+
baseRef.current = composer.getState().text;
|
|
42904
|
+
setError(void 0);
|
|
42905
|
+
try {
|
|
42906
|
+
sessionRef.current = startDictation({
|
|
42907
|
+
lang,
|
|
42908
|
+
onInterim: (text) => aui.thread.composer().setText(joinText(baseRef.current, text)),
|
|
42909
|
+
onFinal: (text) => {
|
|
42910
|
+
baseRef.current = joinText(baseRef.current, text);
|
|
42911
|
+
aui.thread.composer().setText(baseRef.current);
|
|
42912
|
+
},
|
|
42913
|
+
onError: (e) => {
|
|
42914
|
+
setError(messageOf(e));
|
|
42915
|
+
stop();
|
|
42916
|
+
}
|
|
42917
|
+
});
|
|
42918
|
+
setRecording(true);
|
|
42919
|
+
} catch (e) {
|
|
42920
|
+
setError(messageOf(e));
|
|
42921
|
+
}
|
|
42922
|
+
}, [
|
|
42923
|
+
aui,
|
|
42924
|
+
availability,
|
|
42925
|
+
lang,
|
|
42926
|
+
stop
|
|
42927
|
+
]),
|
|
42928
|
+
...error !== void 0 ? { error } : {}
|
|
42929
|
+
};
|
|
42930
|
+
}
|
|
42931
|
+
|
|
42224
42932
|
//#endregion
|
|
42225
42933
|
//#region src/react/AssistantUiShell.tsx
|
|
42934
|
+
/** 识别语言默认跟随界面语言(FR-7.7);SettingsDrawer 里可以覆盖 */
|
|
42935
|
+
const DEFAULT_DICTATION_LANG = {
|
|
42936
|
+
en: "en-US",
|
|
42937
|
+
zh: "zh-CN"
|
|
42938
|
+
};
|
|
42226
42939
|
/**
|
|
42227
42940
|
* assistant-ui only owns the React thread primitives. WebSkill remains the
|
|
42228
42941
|
* source of truth for messages, sessions, Runtime actions, and UI spec surface data.
|
|
@@ -42264,8 +42977,11 @@ function AssistantUiViewport({ children, grow = true }) {
|
|
|
42264
42977
|
children
|
|
42265
42978
|
});
|
|
42266
42979
|
}
|
|
42267
|
-
function AssistantUiComposer({ running, disabled = false, placeholder, attachments = false, centered = false }) {
|
|
42980
|
+
function AssistantUiComposer({ running, disabled = false, placeholder, attachments = false, centered = false, models, dictationLang }) {
|
|
42268
42981
|
const t = useT();
|
|
42982
|
+
const locale = useLocale();
|
|
42983
|
+
const dictation = useComposerDictation(dictationLang ?? DEFAULT_DICTATION_LANG[locale]);
|
|
42984
|
+
const dictationReason = dictation.availability.available ? void 0 : t(`composer.dictate.${dictation.availability.reason}`);
|
|
42269
42985
|
return /* @__PURE__ */ jsx(ComposerPrimitive.Root, {
|
|
42270
42986
|
"data-testid": "assistant-ui-composer",
|
|
42271
42987
|
"data-centered": centered,
|
|
@@ -42273,72 +42989,117 @@ function AssistantUiComposer({ running, disabled = false, placeholder, attachmen
|
|
|
42273
42989
|
children: /* @__PURE__ */ jsxs(ComposerPrimitive.AttachmentDropzone, {
|
|
42274
42990
|
disabled: !attachments,
|
|
42275
42991
|
className: "webskill-aui-composer-shell mx-auto flex w-full max-w-3xl flex-col gap-2 px-3 py-2 transition-colors data-[dragging=true]:border-ring",
|
|
42276
|
-
children: [
|
|
42277
|
-
|
|
42278
|
-
|
|
42279
|
-
|
|
42280
|
-
|
|
42992
|
+
children: [
|
|
42993
|
+
/* @__PURE__ */ jsx("div", {
|
|
42994
|
+
"aria-label": t("composer.attachments"),
|
|
42995
|
+
className: "flex flex-wrap gap-2 empty:hidden",
|
|
42996
|
+
children: /* @__PURE__ */ jsx(ComposerPrimitive.Attachments, { children: ({ attachment }) => /* @__PURE__ */ jsxs(AttachmentPrimitive.Root, {
|
|
42997
|
+
className: "flex max-w-56 items-center gap-1.5 rounded-md border border-border bg-card px-2 py-1 text-xs text-muted",
|
|
42998
|
+
children: [
|
|
42999
|
+
/* @__PURE__ */ jsx(Paperclip, {
|
|
43000
|
+
className: "size-3 shrink-0",
|
|
43001
|
+
"aria-hidden": true
|
|
43002
|
+
}),
|
|
43003
|
+
/* @__PURE__ */ jsx("span", {
|
|
43004
|
+
className: "truncate text-ink",
|
|
43005
|
+
children: /* @__PURE__ */ jsx(AttachmentPrimitive.Name, {})
|
|
43006
|
+
}),
|
|
43007
|
+
/* @__PURE__ */ jsx(AttachmentPrimitive.Remove, {
|
|
43008
|
+
"aria-label": t("composer.removeAttachment", { name: attachment.name }),
|
|
43009
|
+
className: "inline-flex size-4 shrink-0 items-center justify-center rounded-sm hover:text-ink",
|
|
43010
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
43011
|
+
className: "size-3",
|
|
43012
|
+
"aria-hidden": true
|
|
43013
|
+
})
|
|
43014
|
+
})
|
|
43015
|
+
]
|
|
43016
|
+
}) })
|
|
43017
|
+
}),
|
|
43018
|
+
/* @__PURE__ */ jsxs("div", {
|
|
43019
|
+
className: "flex w-full items-end gap-2",
|
|
42281
43020
|
children: [
|
|
42282
|
-
/* @__PURE__ */ jsx(
|
|
42283
|
-
|
|
42284
|
-
"
|
|
43021
|
+
models ? /* @__PURE__ */ jsx("span", {
|
|
43022
|
+
"data-testid": "composer-model-select",
|
|
43023
|
+
className: "shrink-0",
|
|
43024
|
+
children: /* @__PURE__ */ jsx(Select, {
|
|
43025
|
+
className: "w-40",
|
|
43026
|
+
label: t("composer.model"),
|
|
43027
|
+
value: models.value,
|
|
43028
|
+
options: models.options.map((option) => ({
|
|
43029
|
+
value: option.id,
|
|
43030
|
+
label: option.label,
|
|
43031
|
+
...option.disabled === true ? { disabled: true } : {}
|
|
43032
|
+
})),
|
|
43033
|
+
onChange: models.onChange
|
|
43034
|
+
})
|
|
43035
|
+
}) : null,
|
|
43036
|
+
attachments ? /* @__PURE__ */ jsx(ComposerPrimitive.AddAttachment, {
|
|
43037
|
+
"aria-label": t("composer.addAttachment"),
|
|
43038
|
+
className: "inline-flex size-9 shrink-0 items-center justify-center rounded-full text-muted transition-colors hover:bg-accent hover:text-ink",
|
|
43039
|
+
children: /* @__PURE__ */ jsx(Paperclip, {
|
|
43040
|
+
className: "size-4",
|
|
43041
|
+
"aria-hidden": true
|
|
43042
|
+
})
|
|
43043
|
+
}) : null,
|
|
43044
|
+
/* @__PURE__ */ jsxs("button", {
|
|
43045
|
+
type: "button",
|
|
43046
|
+
"data-testid": "composer-dictate",
|
|
43047
|
+
"data-recording": dictation.recording ? "true" : void 0,
|
|
43048
|
+
"aria-pressed": dictation.recording,
|
|
43049
|
+
"aria-label": dictation.recording ? t("composer.dictate.stop") : t("composer.dictate"),
|
|
43050
|
+
title: dictationReason ?? (dictation.recording ? t("composer.dictate.stop") : t("composer.dictate")),
|
|
43051
|
+
disabled: dictationReason !== void 0,
|
|
43052
|
+
onClick: dictation.toggle,
|
|
43053
|
+
className: `inline-flex size-9 shrink-0 items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${dictation.recording ? "animate-pulse bg-danger/10 text-danger" : "text-muted hover:bg-accent hover:text-ink"}`,
|
|
43054
|
+
children: [dictation.recording ? /* @__PURE__ */ jsx(Square, {
|
|
43055
|
+
className: "size-3 fill-current",
|
|
43056
|
+
"aria-hidden": true
|
|
43057
|
+
}) : /* @__PURE__ */ jsx(Mic, {
|
|
43058
|
+
className: "size-4",
|
|
43059
|
+
"aria-hidden": true
|
|
43060
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
43061
|
+
className: "sr-only",
|
|
43062
|
+
children: dictation.recording ? t("composer.dictate.recording") : t("composer.dictate")
|
|
43063
|
+
})]
|
|
42285
43064
|
}),
|
|
42286
|
-
/* @__PURE__ */ jsx(
|
|
42287
|
-
|
|
42288
|
-
|
|
43065
|
+
/* @__PURE__ */ jsx(ComposerPrimitive.Input, {
|
|
43066
|
+
"aria-label": t("composer.inputLabel"),
|
|
43067
|
+
disabled: running || disabled,
|
|
43068
|
+
className: "max-h-40 min-w-0 flex-1 resize-none bg-transparent py-2 text-sm text-ink outline-none placeholder:text-muted/70 disabled:cursor-not-allowed disabled:opacity-60",
|
|
43069
|
+
placeholder: placeholder ?? t("composer.placeholder"),
|
|
43070
|
+
rows: 1,
|
|
43071
|
+
submitMode: "enter"
|
|
42289
43072
|
}),
|
|
42290
|
-
/* @__PURE__ */
|
|
42291
|
-
"aria-label": t("composer.
|
|
42292
|
-
className: "inline-flex size-
|
|
42293
|
-
children: /* @__PURE__ */ jsx(
|
|
42294
|
-
className: "size-3",
|
|
43073
|
+
running ? /* @__PURE__ */ jsxs(ComposerPrimitive.Cancel, {
|
|
43074
|
+
"aria-label": t("composer.stop"),
|
|
43075
|
+
className: "inline-flex size-9 shrink-0 items-center justify-center rounded-full border border-border text-ink transition-colors hover:bg-accent",
|
|
43076
|
+
children: [/* @__PURE__ */ jsx(Square, {
|
|
43077
|
+
className: "size-3 fill-current",
|
|
42295
43078
|
"aria-hidden": true
|
|
42296
|
-
})
|
|
43079
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
43080
|
+
className: "sr-only",
|
|
43081
|
+
children: t("composer.stop")
|
|
43082
|
+
})]
|
|
43083
|
+
}) : /* @__PURE__ */ jsxs(ComposerPrimitive.Send, {
|
|
43084
|
+
"aria-label": t("composer.send"),
|
|
43085
|
+
className: "webskill-aui-send inline-flex size-9 shrink-0 items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50",
|
|
43086
|
+
children: [/* @__PURE__ */ jsx(ArrowUp, {
|
|
43087
|
+
className: "size-4",
|
|
43088
|
+
"aria-hidden": true
|
|
43089
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
43090
|
+
className: "sr-only",
|
|
43091
|
+
children: t("composer.send")
|
|
43092
|
+
})]
|
|
42297
43093
|
})
|
|
42298
43094
|
]
|
|
42299
|
-
})
|
|
42300
|
-
|
|
42301
|
-
|
|
42302
|
-
|
|
42303
|
-
|
|
42304
|
-
|
|
42305
|
-
|
|
42306
|
-
|
|
42307
|
-
className: "size-4",
|
|
42308
|
-
"aria-hidden": true
|
|
42309
|
-
})
|
|
42310
|
-
}) : null,
|
|
42311
|
-
/* @__PURE__ */ jsx(ComposerPrimitive.Input, {
|
|
42312
|
-
"aria-label": t("composer.inputLabel"),
|
|
42313
|
-
disabled: running || disabled,
|
|
42314
|
-
className: "max-h-40 min-w-0 flex-1 resize-none bg-transparent py-2 text-sm text-ink outline-none placeholder:text-muted/70 disabled:cursor-not-allowed disabled:opacity-60",
|
|
42315
|
-
placeholder: placeholder ?? t("composer.placeholder"),
|
|
42316
|
-
rows: 1,
|
|
42317
|
-
submitMode: "enter"
|
|
42318
|
-
}),
|
|
42319
|
-
running ? /* @__PURE__ */ jsxs(ComposerPrimitive.Cancel, {
|
|
42320
|
-
"aria-label": t("composer.stop"),
|
|
42321
|
-
className: "inline-flex size-9 shrink-0 items-center justify-center rounded-full border border-border text-ink transition-colors hover:bg-accent",
|
|
42322
|
-
children: [/* @__PURE__ */ jsx(Square, {
|
|
42323
|
-
className: "size-3 fill-current",
|
|
42324
|
-
"aria-hidden": true
|
|
42325
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
42326
|
-
className: "sr-only",
|
|
42327
|
-
children: t("composer.stop")
|
|
42328
|
-
})]
|
|
42329
|
-
}) : /* @__PURE__ */ jsxs(ComposerPrimitive.Send, {
|
|
42330
|
-
"aria-label": t("composer.send"),
|
|
42331
|
-
className: "webskill-aui-send inline-flex size-9 shrink-0 items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50",
|
|
42332
|
-
children: [/* @__PURE__ */ jsx(ArrowUp, {
|
|
42333
|
-
className: "size-4",
|
|
42334
|
-
"aria-hidden": true
|
|
42335
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
42336
|
-
className: "sr-only",
|
|
42337
|
-
children: t("composer.send")
|
|
42338
|
-
})]
|
|
42339
|
-
})
|
|
42340
|
-
]
|
|
42341
|
-
})]
|
|
43095
|
+
}),
|
|
43096
|
+
dictation.error !== void 0 ? /* @__PURE__ */ jsx("p", {
|
|
43097
|
+
"data-testid": "composer-dictate-error",
|
|
43098
|
+
role: "alert",
|
|
43099
|
+
className: "text-xs text-danger",
|
|
43100
|
+
children: dictation.error
|
|
43101
|
+
}) : null
|
|
43102
|
+
]
|
|
42342
43103
|
})
|
|
42343
43104
|
});
|
|
42344
43105
|
}
|
|
@@ -42608,6 +43369,38 @@ function AssistantUiEmptyState({ title, prompts, disabled, onPrompt }) {
|
|
|
42608
43369
|
});
|
|
42609
43370
|
}
|
|
42610
43371
|
|
|
43372
|
+
//#endregion
|
|
43373
|
+
//#region src/react/aui/PerceptionNotice.tsx
|
|
43374
|
+
function readStringList(source, key) {
|
|
43375
|
+
const value = source?.[key];
|
|
43376
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
43377
|
+
}
|
|
43378
|
+
/**
|
|
43379
|
+
* 页面只读感知提示(需求 10 FR-10.5)。刻意不进思维链折叠区:
|
|
43380
|
+
* 「读了页面」这件事必须默认可见,附上实际范围让用户能发现读得比预期多。
|
|
43381
|
+
*/
|
|
43382
|
+
function PerceptionNotice({ data }) {
|
|
43383
|
+
const t = useT();
|
|
43384
|
+
const include = readStringList(data, "include");
|
|
43385
|
+
if (include.length === 0) return null;
|
|
43386
|
+
const exclude = readStringList(data, "exclude");
|
|
43387
|
+
return /* @__PURE__ */ jsxs("p", {
|
|
43388
|
+
"data-testid": "perception-notice",
|
|
43389
|
+
className: "my-1 flex flex-wrap items-center gap-2 rounded-md border border-border bg-card px-2 py-1 text-[11px] text-muted",
|
|
43390
|
+
children: [
|
|
43391
|
+
/* @__PURE__ */ jsx(Eye, {
|
|
43392
|
+
className: "size-3 shrink-0",
|
|
43393
|
+
"aria-hidden": true
|
|
43394
|
+
}),
|
|
43395
|
+
/* @__PURE__ */ jsx("span", {
|
|
43396
|
+
className: "text-ink",
|
|
43397
|
+
children: t("perception.reading", { scope: include.join(", ") })
|
|
43398
|
+
}),
|
|
43399
|
+
exclude.length > 0 ? /* @__PURE__ */ jsx("span", { children: t("perception.excluded", { scope: exclude.join(", ") }) }) : null
|
|
43400
|
+
]
|
|
43401
|
+
});
|
|
43402
|
+
}
|
|
43403
|
+
|
|
42611
43404
|
//#endregion
|
|
42612
43405
|
//#region src/react/aui/parts.tsx
|
|
42613
43406
|
/** 模块顶层稳定引用:GroupedParts 用它做记忆指纹 */
|
|
@@ -42754,12 +43547,26 @@ function ToolGroup({ count, running, children }) {
|
|
|
42754
43547
|
}), children]
|
|
42755
43548
|
});
|
|
42756
43549
|
}
|
|
43550
|
+
/** 被 `allowed-tools` 拦下时 runtime 回喂的错误码;与执行失败必须分开显示(需求 1 FR-1.6) */
|
|
43551
|
+
const DENIED_CODE = "TOOL_NOT_ALLOWED";
|
|
43552
|
+
function toolErrorOf(part) {
|
|
43553
|
+
const result = part.result;
|
|
43554
|
+
if (typeof result !== "object" || result === null) return {};
|
|
43555
|
+
const record = result;
|
|
43556
|
+
return {
|
|
43557
|
+
...typeof record["errorCode"] === "string" ? { errorCode: record["errorCode"] } : {},
|
|
43558
|
+
...typeof record["errorMessage"] === "string" ? { errorMessage: record["errorMessage"] } : {}
|
|
43559
|
+
};
|
|
43560
|
+
}
|
|
42757
43561
|
/** 工具行:状态 / 耗时 / 参数摘要。工具返回值不在 chatbot 数据面内,因此不渲染结果区。 */
|
|
42758
43562
|
function ToolRow({ part }) {
|
|
42759
43563
|
const t = useT();
|
|
42760
43564
|
const [open, setOpen] = useState(false);
|
|
42761
43565
|
const completedAt = part.timing?.completedAt;
|
|
42762
|
-
const
|
|
43566
|
+
const running = completedAt === void 0 && isRunning(part.status);
|
|
43567
|
+
const { errorCode, errorMessage } = toolErrorOf(part);
|
|
43568
|
+
const denied = part.isError === true && errorCode === DENIED_CODE;
|
|
43569
|
+
const status = running ? "running" : denied ? "denied" : part.isError === true ? "failed" : "completed";
|
|
42763
43570
|
const duration = part.timing && completedAt !== void 0 ? formatDuration(completedAt - part.timing.startedAt) : void 0;
|
|
42764
43571
|
return /* @__PURE__ */ jsxs("div", {
|
|
42765
43572
|
className: "rounded-md border border-border",
|
|
@@ -42775,6 +43582,9 @@ function ToolRow({ part }) {
|
|
|
42775
43582
|
}) : status === "completed" ? /* @__PURE__ */ jsx(CircleCheck, {
|
|
42776
43583
|
className: "size-3.5 shrink-0 text-success",
|
|
42777
43584
|
"aria-label": t("tool.status.completed")
|
|
43585
|
+
}) : status === "denied" ? /* @__PURE__ */ jsx(ShieldOff, {
|
|
43586
|
+
className: "size-3.5 shrink-0 text-warning",
|
|
43587
|
+
"aria-label": t("tool.status.denied")
|
|
42778
43588
|
}) : /* @__PURE__ */ jsx(CircleX, {
|
|
42779
43589
|
className: "size-3.5 shrink-0 text-destructive",
|
|
42780
43590
|
"aria-label": t("tool.status.failed")
|
|
@@ -42783,6 +43593,11 @@ function ToolRow({ part }) {
|
|
|
42783
43593
|
className: "truncate font-mono text-ink",
|
|
42784
43594
|
children: part.toolName
|
|
42785
43595
|
}),
|
|
43596
|
+
status === "denied" ? /* @__PURE__ */ jsx("span", {
|
|
43597
|
+
"data-testid": "tool-denied",
|
|
43598
|
+
className: "shrink-0 text-[11px] text-warning",
|
|
43599
|
+
children: t("tool.status.denied")
|
|
43600
|
+
}) : null,
|
|
42786
43601
|
/* @__PURE__ */ jsx("span", {
|
|
42787
43602
|
className: "ms-auto shrink-0",
|
|
42788
43603
|
children: duration ?? ""
|
|
@@ -42792,15 +43607,22 @@ function ToolRow({ part }) {
|
|
|
42792
43607
|
"aria-hidden": true
|
|
42793
43608
|
})
|
|
42794
43609
|
]
|
|
42795
|
-
}), open && part.argsText !== "" ? /* @__PURE__ */ jsxs("div", {
|
|
43610
|
+
}), open && (part.argsText !== "" || errorMessage !== void 0) ? /* @__PURE__ */ jsxs("div", {
|
|
42796
43611
|
className: "border-t border-border px-2 py-1.5",
|
|
42797
|
-
children: [/* @__PURE__ */ jsx("p", {
|
|
43612
|
+
children: [errorMessage !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
|
|
43613
|
+
className: "text-[11px] text-muted",
|
|
43614
|
+
children: t(denied ? "tool.denied.reason" : "tool.failed.reason")
|
|
43615
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
43616
|
+
"data-testid": "tool-error-reason",
|
|
43617
|
+
className: "text-[11px] break-words text-ink",
|
|
43618
|
+
children: errorMessage
|
|
43619
|
+
})] }) : null, part.argsText !== "" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
|
|
42798
43620
|
className: "text-[11px] text-muted",
|
|
42799
43621
|
children: t("tool.args")
|
|
42800
43622
|
}), /* @__PURE__ */ jsx("pre", {
|
|
42801
43623
|
className: "overflow-x-auto font-mono text-[11px] text-ink",
|
|
42802
43624
|
children: part.argsText
|
|
42803
|
-
})]
|
|
43625
|
+
})] }) : null]
|
|
42804
43626
|
}) : null]
|
|
42805
43627
|
});
|
|
42806
43628
|
}
|
|
@@ -42856,6 +43678,7 @@ function WebSkillMessageParts() {
|
|
|
42856
43678
|
if (part.dataRendererUI) return part.dataRendererUI;
|
|
42857
43679
|
if (part.name === "webskill.lifecycle") return /* @__PURE__ */ jsx(LifecycleStep, { data: part.data });
|
|
42858
43680
|
if (part.name === "webskill.skill") return /* @__PURE__ */ jsx(SkillActivation, { data: part.data });
|
|
43681
|
+
if (part.name === "webskill.perception") return /* @__PURE__ */ jsx(PerceptionNotice, { data: part.data });
|
|
42859
43682
|
return null;
|
|
42860
43683
|
case "indicator": return /* @__PURE__ */ jsx(WorkingIndicator, {});
|
|
42861
43684
|
default: return null;
|
|
@@ -42864,6 +43687,104 @@ function WebSkillMessageParts() {
|
|
|
42864
43687
|
});
|
|
42865
43688
|
}
|
|
42866
43689
|
|
|
43690
|
+
//#endregion
|
|
43691
|
+
//#region src/react/TodoPanel.tsx
|
|
43692
|
+
const STATUS_KEY = {
|
|
43693
|
+
pending: "todo.status.pending",
|
|
43694
|
+
"in-progress": "todo.status.inProgress",
|
|
43695
|
+
completed: "todo.status.completed"
|
|
43696
|
+
};
|
|
43697
|
+
/** 状态标记:✓ 已完成 / ● 进行中(脉冲)/ ○ 待开始(设计 03 §9) */
|
|
43698
|
+
function StatusMark({ status }) {
|
|
43699
|
+
if (status === "completed") return /* @__PURE__ */ jsx("span", {
|
|
43700
|
+
"aria-hidden": true,
|
|
43701
|
+
className: "w-3.5 shrink-0 text-center text-success",
|
|
43702
|
+
children: "✓"
|
|
43703
|
+
});
|
|
43704
|
+
if (status === "in-progress") return /* @__PURE__ */ jsx("span", {
|
|
43705
|
+
"aria-hidden": true,
|
|
43706
|
+
className: "w-3.5 shrink-0 animate-pulse text-center text-info",
|
|
43707
|
+
children: "●"
|
|
43708
|
+
});
|
|
43709
|
+
return /* @__PURE__ */ jsx("span", {
|
|
43710
|
+
"aria-hidden": true,
|
|
43711
|
+
className: "w-3.5 shrink-0 text-center text-muted",
|
|
43712
|
+
children: "○"
|
|
43713
|
+
});
|
|
43714
|
+
}
|
|
43715
|
+
function TodoRow({ item }) {
|
|
43716
|
+
const t = useT();
|
|
43717
|
+
const statusLabel = t(STATUS_KEY[item.status]);
|
|
43718
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
43719
|
+
"data-testid": "todo-item",
|
|
43720
|
+
"data-status": item.status,
|
|
43721
|
+
"aria-label": `${item.title}, ${statusLabel}`,
|
|
43722
|
+
className: "flex items-center gap-2 text-xs",
|
|
43723
|
+
children: [
|
|
43724
|
+
/* @__PURE__ */ jsx(StatusMark, { status: item.status }),
|
|
43725
|
+
/* @__PURE__ */ jsx("span", {
|
|
43726
|
+
className: `min-w-0 truncate ${item.status === "completed" ? "text-muted line-through" : "text-ink"}`,
|
|
43727
|
+
children: item.title
|
|
43728
|
+
}),
|
|
43729
|
+
item.delegatedTo ? /* @__PURE__ */ jsx("span", {
|
|
43730
|
+
className: "ms-auto shrink-0 truncate text-[11px] text-muted",
|
|
43731
|
+
children: t("todo.delegatedTo", { skill: item.delegatedTo })
|
|
43732
|
+
}) : null
|
|
43733
|
+
]
|
|
43734
|
+
});
|
|
43735
|
+
}
|
|
43736
|
+
/**
|
|
43737
|
+
* 待办清单面板:随消息流内联渲染(不是固定侧栏,设计 03 §9)。
|
|
43738
|
+
* 全部完成后自动折叠,用户手动切换后不再被自动接管。
|
|
43739
|
+
*/
|
|
43740
|
+
function TodoPanel({ items }) {
|
|
43741
|
+
const t = useT();
|
|
43742
|
+
const done = items.filter((item) => item.status === "completed").length;
|
|
43743
|
+
const running = done < items.length;
|
|
43744
|
+
const [open, setOpen] = useState(running);
|
|
43745
|
+
const [pinned, setPinned] = useState(false);
|
|
43746
|
+
useEffect(() => {
|
|
43747
|
+
if (!pinned) setOpen(running);
|
|
43748
|
+
}, [running, pinned]);
|
|
43749
|
+
if (items.length === 0) return null;
|
|
43750
|
+
return /* @__PURE__ */ jsxs(Collapsible, {
|
|
43751
|
+
open,
|
|
43752
|
+
onOpenChange: (next) => {
|
|
43753
|
+
setPinned(true);
|
|
43754
|
+
setOpen(next);
|
|
43755
|
+
},
|
|
43756
|
+
"data-testid": "todo-panel",
|
|
43757
|
+
className: "my-2 overflow-hidden rounded-lg border border-border bg-card",
|
|
43758
|
+
children: [/* @__PURE__ */ jsxs(CollapsibleTrigger, {
|
|
43759
|
+
className: "flex w-full items-center gap-2 px-3 py-2 text-left text-xs text-muted transition-colors hover:bg-accent",
|
|
43760
|
+
children: [
|
|
43761
|
+
/* @__PURE__ */ jsx(ChevronDown, {
|
|
43762
|
+
className: `size-3.5 shrink-0 transition-transform ${open ? "rotate-180" : ""}`,
|
|
43763
|
+
"aria-hidden": true
|
|
43764
|
+
}),
|
|
43765
|
+
/* @__PURE__ */ jsx("span", {
|
|
43766
|
+
className: "flex-1 truncate text-ink",
|
|
43767
|
+
children: t("todo.title")
|
|
43768
|
+
}),
|
|
43769
|
+
/* @__PURE__ */ jsx("span", {
|
|
43770
|
+
"data-testid": "todo-progress",
|
|
43771
|
+
className: "shrink-0 font-mono",
|
|
43772
|
+
children: t("todo.progress", {
|
|
43773
|
+
done: String(done),
|
|
43774
|
+
total: String(items.length)
|
|
43775
|
+
})
|
|
43776
|
+
})
|
|
43777
|
+
]
|
|
43778
|
+
}), /* @__PURE__ */ jsx(CollapsibleContent, {
|
|
43779
|
+
className: "border-t border-border px-3 py-2",
|
|
43780
|
+
children: /* @__PURE__ */ jsx("ol", {
|
|
43781
|
+
className: "flex flex-col gap-1",
|
|
43782
|
+
children: items.map((item) => /* @__PURE__ */ jsx(TodoRow, { item }, item.id))
|
|
43783
|
+
})
|
|
43784
|
+
})]
|
|
43785
|
+
});
|
|
43786
|
+
}
|
|
43787
|
+
|
|
42867
43788
|
//#endregion
|
|
42868
43789
|
//#region src/react/OpenUiSurfaceHost.tsx
|
|
42869
43790
|
/** Renders persisted UI spec snapshots through the OpenUI extension without a live bridge. @experimental */
|
|
@@ -53079,7 +54000,7 @@ function SkillBadges({ skills }) {
|
|
|
53079
54000
|
})
|
|
53080
54001
|
})
|
|
53081
54002
|
}), skills.map((skill) => /* @__PURE__ */ jsx(Badge, {
|
|
53082
|
-
tone: "
|
|
54003
|
+
tone: "neutral",
|
|
53083
54004
|
children: skill
|
|
53084
54005
|
}, skill))]
|
|
53085
54006
|
});
|
|
@@ -53243,7 +54164,9 @@ function AssistantUiMessageActions({ message, onRetry, onDelete, onViewTrace })
|
|
|
53243
54164
|
/** assistant-ui Base message layout with WebSkill-only capabilities mounted as secondary extensions. */
|
|
53244
54165
|
function AssistantUiMessage(props) {
|
|
53245
54166
|
const { message } = props;
|
|
53246
|
-
|
|
54167
|
+
const role = useAuiState((s) => s.message.role);
|
|
54168
|
+
const todos = useAuiState((s) => s.message.metadata?.custom?.["todos"]);
|
|
54169
|
+
if (role === "user") return /* @__PURE__ */ jsxs("div", {
|
|
53247
54170
|
className: "webskill-aui-message webskill-aui-message--user",
|
|
53248
54171
|
"data-testid": "assistant-ui-user-message",
|
|
53249
54172
|
children: [/* @__PURE__ */ jsx("div", {
|
|
@@ -53255,6 +54178,7 @@ function AssistantUiMessage(props) {
|
|
|
53255
54178
|
className: "webskill-aui-message webskill-aui-message--assistant min-w-0",
|
|
53256
54179
|
"data-testid": "assistant-ui-assistant-message",
|
|
53257
54180
|
children: [
|
|
54181
|
+
Array.isArray(todos) ? /* @__PURE__ */ jsx(TodoPanel, { items: todos }) : null,
|
|
53258
54182
|
/* @__PURE__ */ jsx("div", {
|
|
53259
54183
|
className: "webskill-aui-assistant-content",
|
|
53260
54184
|
children: /* @__PURE__ */ jsx(WebSkillMessageParts, {})
|
|
@@ -53389,6 +54313,8 @@ const start = (runId, at) => ({
|
|
|
53389
54313
|
phases: [],
|
|
53390
54314
|
skills: [],
|
|
53391
54315
|
tools: [],
|
|
54316
|
+
todos: [],
|
|
54317
|
+
perceptions: [],
|
|
53392
54318
|
text: "",
|
|
53393
54319
|
chunks: 0
|
|
53394
54320
|
});
|
|
@@ -53439,6 +54365,14 @@ function reduceLiveRun(live, event, at) {
|
|
|
53439
54365
|
...live,
|
|
53440
54366
|
skills: event.skills
|
|
53441
54367
|
};
|
|
54368
|
+
case "todo-changed": return {
|
|
54369
|
+
...live,
|
|
54370
|
+
todos: event.items
|
|
54371
|
+
};
|
|
54372
|
+
case "page-perceived": return {
|
|
54373
|
+
...live,
|
|
54374
|
+
perceptions: [...live.perceptions, event.record]
|
|
54375
|
+
};
|
|
53442
54376
|
case "message-delta": return {
|
|
53443
54377
|
...live,
|
|
53444
54378
|
text: live.text + event.delta,
|
|
@@ -53458,12 +54392,14 @@ function reduceLiveRun(live, event, at) {
|
|
|
53458
54392
|
case "tool-completed":
|
|
53459
54393
|
case "tool-failed": {
|
|
53460
54394
|
const status = event.type === "tool-completed" ? "completed" : "failed";
|
|
54395
|
+
const errorCode = event.type === "tool-failed" ? event.errorCode : void 0;
|
|
53461
54396
|
return {
|
|
53462
54397
|
...live,
|
|
53463
54398
|
tools: live.tools.map((tool) => tool.callId === event.callId && tool.status === "running" ? {
|
|
53464
54399
|
...tool,
|
|
53465
54400
|
status,
|
|
53466
|
-
endedAt: at
|
|
54401
|
+
endedAt: at,
|
|
54402
|
+
...errorCode !== void 0 ? { errorCode } : {}
|
|
53467
54403
|
} : tool)
|
|
53468
54404
|
};
|
|
53469
54405
|
}
|
|
@@ -53576,6 +54512,7 @@ function ActiveInteractionCard({ request, bridge }) {
|
|
|
53576
54512
|
const spec = interactionToUiSpec(request, interactionLabels(request, t));
|
|
53577
54513
|
const isAuthorize = request.type === "authorize";
|
|
53578
54514
|
const Icon = kindIcons[request.type];
|
|
54515
|
+
const preview = isAuthorize && typeof request.details === "object" && request.details !== null ? request.details["preview"] : void 0;
|
|
53579
54516
|
const handleAction = (event) => {
|
|
53580
54517
|
if (event.intent !== "submit") {
|
|
53581
54518
|
bridge.resolve({
|
|
@@ -53593,49 +54530,61 @@ function ActiveInteractionCard({ request, bridge }) {
|
|
|
53593
54530
|
return /* @__PURE__ */ jsxs(motion.div, {
|
|
53594
54531
|
...fadeInUp,
|
|
53595
54532
|
className: `rounded-card border shadow-card ${isAuthorize ? "border-warning/50 bg-warning-soft" : "border-border bg-surface"}`,
|
|
53596
|
-
children: [
|
|
53597
|
-
|
|
53598
|
-
|
|
53599
|
-
|
|
53600
|
-
|
|
53601
|
-
|
|
53602
|
-
"
|
|
54533
|
+
children: [
|
|
54534
|
+
request.origin ? /* @__PURE__ */ jsx("div", {
|
|
54535
|
+
className: "px-4 pt-3",
|
|
54536
|
+
children: /* @__PURE__ */ jsx(Badge, {
|
|
54537
|
+
tone: "info",
|
|
54538
|
+
"data-testid": "interaction-origin",
|
|
54539
|
+
children: t("interaction.origin", { label: request.origin.label })
|
|
54540
|
+
})
|
|
54541
|
+
}) : null,
|
|
54542
|
+
/* @__PURE__ */ jsxs("div", {
|
|
54543
|
+
className: "flex items-start gap-3 px-4 pt-4",
|
|
54544
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
54545
|
+
className: `grid size-8 shrink-0 place-items-center rounded-lg ${isAuthorize ? "bg-warning/15 text-warning" : "bg-subtle text-muted"}`,
|
|
54546
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
54547
|
+
className: "size-4",
|
|
54548
|
+
"aria-hidden": true
|
|
54549
|
+
})
|
|
54550
|
+
}), request.type === "authorize" ? /* @__PURE__ */ jsxs("div", {
|
|
54551
|
+
className: "flex min-w-0 flex-1 flex-col gap-2",
|
|
54552
|
+
children: [
|
|
54553
|
+
/* @__PURE__ */ jsxs("div", {
|
|
54554
|
+
className: "flex items-center gap-2",
|
|
54555
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
54556
|
+
className: "text-xs font-medium text-muted",
|
|
54557
|
+
children: t("interaction.authorize.capability")
|
|
54558
|
+
}), /* @__PURE__ */ jsx(Badge, {
|
|
54559
|
+
tone: "warning",
|
|
54560
|
+
children: request.capability
|
|
54561
|
+
})]
|
|
54562
|
+
}),
|
|
54563
|
+
/* @__PURE__ */ jsx("p", {
|
|
54564
|
+
className: "text-xs text-muted",
|
|
54565
|
+
children: t("interaction.authorize.description")
|
|
54566
|
+
}),
|
|
54567
|
+
request.details !== void 0 ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
54568
|
+
className: "mb-1 text-xs font-medium text-muted",
|
|
54569
|
+
children: t("interaction.authorize.details")
|
|
54570
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
54571
|
+
"data-testid": "interaction-authorize-details",
|
|
54572
|
+
className: "max-h-64 overflow-auto rounded-lg border border-border bg-canvas p-3 font-mono text-xs whitespace-pre-wrap text-ink",
|
|
54573
|
+
children: typeof preview === "string" ? preview : JSON.stringify(request.details, null, 2)
|
|
54574
|
+
})] }) : null
|
|
54575
|
+
]
|
|
54576
|
+
}) : null]
|
|
54577
|
+
}),
|
|
54578
|
+
/* @__PURE__ */ jsx("div", {
|
|
54579
|
+
className: "px-4 py-4",
|
|
54580
|
+
children: /* @__PURE__ */ jsx(NativeSpecSurface, {
|
|
54581
|
+
surfaceId: request.id,
|
|
54582
|
+
spec,
|
|
54583
|
+
actions: INTERACTION_ACTIONS$1,
|
|
54584
|
+
onAction: handleAction
|
|
53603
54585
|
})
|
|
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]
|
|
53630
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
53631
|
-
className: "px-4 py-4",
|
|
53632
|
-
children: /* @__PURE__ */ jsx(NativeSpecSurface, {
|
|
53633
|
-
surfaceId: request.id,
|
|
53634
|
-
spec,
|
|
53635
|
-
actions: INTERACTION_ACTIONS$1,
|
|
53636
|
-
onAction: handleAction
|
|
53637
54586
|
})
|
|
53638
|
-
|
|
54587
|
+
]
|
|
53639
54588
|
});
|
|
53640
54589
|
}
|
|
53641
54590
|
|
|
@@ -53697,6 +54646,23 @@ function InterruptedBanner({ engine, sessionId }) {
|
|
|
53697
54646
|
});
|
|
53698
54647
|
}
|
|
53699
54648
|
|
|
54649
|
+
//#endregion
|
|
54650
|
+
//#region src/react/rendererProbes.ts
|
|
54651
|
+
/**
|
|
54652
|
+
* optional peer 探测:缺包时交互通道据此粘性降级 native,设置面板据此把档位置灰。
|
|
54653
|
+
* 两处必须用同一份探测——否则会出现「设置里能选,用完才降级」的假可选项。
|
|
54654
|
+
*/
|
|
54655
|
+
const RENDERER_PROBES = {
|
|
54656
|
+
a2ui: probeA2uiAvailability,
|
|
54657
|
+
openui: probeOpenUiAvailability,
|
|
54658
|
+
vercel: probeJsonRenderAvailability
|
|
54659
|
+
};
|
|
54660
|
+
const MISSING_PEER = {
|
|
54661
|
+
a2ui: "@a2ui/lit and @a2ui/web_core are not installed in this host",
|
|
54662
|
+
openui: "@openuidev/react-lang is not installed in this host",
|
|
54663
|
+
vercel: "@json-render/core and @json-render/react are not installed in this host"
|
|
54664
|
+
};
|
|
54665
|
+
|
|
53700
54666
|
//#endregion
|
|
53701
54667
|
//#region src/react/SpecInteraction.tsx
|
|
53702
54668
|
/** 交互表单的两个动作:与 surface 侧共用 catalog 的 submit / cancel 语义 */
|
|
@@ -53713,17 +54679,6 @@ const SURFACES = {
|
|
|
53713
54679
|
openui: OpenUiSpecSurface,
|
|
53714
54680
|
vercel: JsonRenderSpecSurface
|
|
53715
54681
|
};
|
|
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
54682
|
/**
|
|
53728
54683
|
* 非 native 三档的交互视图(路径 A):订阅 SpecInteractionChannel(core 产出 catalog
|
|
53729
54684
|
* 节点树),交给本档 surface 侧同一个渲染入口渲染;提交/取消经 handleAction 回传。
|
|
@@ -53742,7 +54697,7 @@ function SpecInteraction({ channel, renderer }) {
|
|
|
53742
54697
|
}, [channel]);
|
|
53743
54698
|
useEffect(() => {
|
|
53744
54699
|
let cancelled = false;
|
|
53745
|
-
|
|
54700
|
+
RENDERER_PROBES[renderer]().then((available) => {
|
|
53746
54701
|
if (!cancelled && !available) channel.fail(MISSING_PEER[renderer]);
|
|
53747
54702
|
});
|
|
53748
54703
|
return () => {
|
|
@@ -53764,63 +54719,99 @@ function SpecInteraction({ channel, renderer }) {
|
|
|
53764
54719
|
const PROVIDERS = [
|
|
53765
54720
|
"openai-compatible",
|
|
53766
54721
|
"anthropic",
|
|
53767
|
-
"google"
|
|
54722
|
+
"google",
|
|
54723
|
+
"chrome-builtin"
|
|
53768
54724
|
];
|
|
53769
|
-
|
|
53770
|
-
|
|
53771
|
-
|
|
53772
|
-
|
|
53773
|
-
|
|
53774
|
-
|
|
53775
|
-
|
|
53776
|
-
|
|
53777
|
-
|
|
53778
|
-
|
|
53779
|
-
|
|
53780
|
-
|
|
53781
|
-
|
|
53782
|
-
|
|
53783
|
-
|
|
53784
|
-
|
|
53785
|
-
|
|
53786
|
-
|
|
54725
|
+
const EMPTY_FORM = {
|
|
54726
|
+
label: "",
|
|
54727
|
+
provider: "openai-compatible",
|
|
54728
|
+
baseUrl: "",
|
|
54729
|
+
apiKey: "",
|
|
54730
|
+
model: ""
|
|
54731
|
+
};
|
|
54732
|
+
const toForm = (entry) => ({
|
|
54733
|
+
label: entry.label,
|
|
54734
|
+
provider: entry.provider,
|
|
54735
|
+
baseUrl: entry.baseUrl ?? "",
|
|
54736
|
+
apiKey: entry.apiKey ?? "",
|
|
54737
|
+
model: entry.model
|
|
54738
|
+
});
|
|
54739
|
+
const RENDERERS = [
|
|
54740
|
+
"native",
|
|
54741
|
+
"a2ui",
|
|
54742
|
+
"openui",
|
|
54743
|
+
"vercel"
|
|
54744
|
+
];
|
|
54745
|
+
/**
|
|
54746
|
+
* 识别语言候选(FR-7.7)。只给常用几种:完整 BCP-47 列表有上百项,
|
|
54747
|
+
* 而浏览器实际支持哪些不可探测,列全了反而是假选项。
|
|
54748
|
+
*/
|
|
54749
|
+
const DICTATION_LANGS = [
|
|
54750
|
+
"en-US",
|
|
54751
|
+
"en-GB",
|
|
54752
|
+
"zh-CN",
|
|
54753
|
+
"zh-TW",
|
|
54754
|
+
"ja-JP",
|
|
54755
|
+
"ko-KR",
|
|
54756
|
+
"de-DE",
|
|
54757
|
+
"fr-FR",
|
|
54758
|
+
"es-ES"
|
|
54759
|
+
];
|
|
54760
|
+
/** id 只在新建时生成一次:条目被引用后(会话记录里的模型归属)改 id 等于断链 */
|
|
54761
|
+
const newEntryId = () => `llm-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
54762
|
+
/** 默认落盘:触发一次浏览器下载。jsdom 等无 `createObjectURL` 的环境下静默跳过 */
|
|
54763
|
+
function downloadPreferences(data) {
|
|
54764
|
+
if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return;
|
|
54765
|
+
const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
|
|
54766
|
+
const url = URL.createObjectURL(blob);
|
|
54767
|
+
const link = document.createElement("a");
|
|
54768
|
+
link.href = url;
|
|
54769
|
+
link.download = "webskill-preferences.json";
|
|
54770
|
+
link.click();
|
|
54771
|
+
URL.revokeObjectURL(url);
|
|
54772
|
+
}
|
|
54773
|
+
/** 设置抽屉:模型列表(增删改 / 设默认 / 逐条探测)+ 流式与 temperature + Agent 能力 + 外观 @stable */
|
|
54774
|
+
function SettingsDrawer({ open, runtimeConfig, theme, locale, renderer, dictationLang = "", onAppearanceChange, onSaved, onClearFormAutofill, onExportPreferences, onClose }) {
|
|
53787
54775
|
const t = useT();
|
|
53788
|
-
const [
|
|
53789
|
-
const [
|
|
53790
|
-
|
|
53791
|
-
const [
|
|
54776
|
+
const [entries, setEntries] = useState([]);
|
|
54777
|
+
const [defaultId, setDefaultId] = useState();
|
|
54778
|
+
/** 正在编辑的条目 id;'new' 表示新增中;undefined 表示只看列表 */
|
|
54779
|
+
const [editing, setEditing] = useState();
|
|
54780
|
+
const [form, setForm] = useState(EMPTY_FORM);
|
|
53792
54781
|
const [streaming, setStreaming] = useState(true);
|
|
53793
54782
|
const [temperature, setTemperature] = useState("");
|
|
54783
|
+
const [todoEnabled, setTodoEnabled] = useState(true);
|
|
54784
|
+
const [skillGenerationEnabled, setSkillGenerationEnabled] = useState(false);
|
|
54785
|
+
const [generativeUiEnabled, setGenerativeUiEnabled] = useState(false);
|
|
54786
|
+
const [formAutofillEnabled, setFormAutofillEnabled] = useState(false);
|
|
54787
|
+
const [delegationEnabled, setDelegationEnabled] = useState(false);
|
|
54788
|
+
const [autofillCleared, setAutofillCleared] = useState(false);
|
|
54789
|
+
const [prefNotice, setPrefNotice] = useState();
|
|
53794
54790
|
const [loaded, setLoaded] = useState(false);
|
|
53795
54791
|
const [error, setError] = useState();
|
|
53796
|
-
const [
|
|
54792
|
+
const [probes, setProbes] = useState({});
|
|
54793
|
+
const [chrome, setChrome] = useState();
|
|
54794
|
+
const [rendererPeers, setRendererPeers] = useState({});
|
|
53797
54795
|
const configStore = useMemo(() => runtimeConfig ? withRuntimeConfigDefaults(runtimeConfig) : void 0, [runtimeConfig]);
|
|
53798
54796
|
useEffect(() => {
|
|
53799
|
-
if (!open) return;
|
|
54797
|
+
if (!open || !configStore) return;
|
|
53800
54798
|
let cancelled = false;
|
|
53801
54799
|
setLoaded(false);
|
|
53802
|
-
|
|
53803
|
-
if (configStore) {
|
|
53804
|
-
const rc = await configStore.load();
|
|
53805
|
-
setProvider(rc.llm.provider);
|
|
53806
|
-
setBaseUrl(rc.llm.baseUrl ?? "");
|
|
53807
|
-
setApiKey(rc.llm.apiKey ?? "");
|
|
53808
|
-
setModel(rc.llm.model);
|
|
53809
|
-
setStreaming(rc.streaming);
|
|
53810
|
-
setTemperature(rc.loop.temperature !== void 0 ? String(rc.loop.temperature) : "");
|
|
53811
|
-
} else {
|
|
53812
|
-
const config = await llmConfig.load();
|
|
53813
|
-
setProvider("openai-compatible");
|
|
53814
|
-
setBaseUrl(config?.baseUrl ?? "");
|
|
53815
|
-
setApiKey(config?.apiKey ?? "");
|
|
53816
|
-
setModel(config?.model ?? "");
|
|
53817
|
-
setStreaming(true);
|
|
53818
|
-
setTemperature("");
|
|
53819
|
-
}
|
|
53820
|
-
};
|
|
53821
|
-
load().then(() => {
|
|
54800
|
+
configStore.load().then((rc) => {
|
|
53822
54801
|
if (cancelled) return;
|
|
53823
|
-
|
|
54802
|
+
setEntries([...rc.llm.entries]);
|
|
54803
|
+
setDefaultId(rc.llm.defaultId);
|
|
54804
|
+
setStreaming(rc.streaming);
|
|
54805
|
+
setTemperature(rc.loop.temperature !== void 0 ? String(rc.loop.temperature) : "");
|
|
54806
|
+
setTodoEnabled(rc.agentCapabilities.todo);
|
|
54807
|
+
setSkillGenerationEnabled(rc.agentCapabilities.skillGeneration);
|
|
54808
|
+
setGenerativeUiEnabled(rc.agentCapabilities.generativeUi);
|
|
54809
|
+
setFormAutofillEnabled(rc.agentCapabilities.formAutofill);
|
|
54810
|
+
setDelegationEnabled(rc.agentCapabilities.delegation);
|
|
54811
|
+
setAutofillCleared(false);
|
|
54812
|
+
setPrefNotice(void 0);
|
|
54813
|
+
setEditing(void 0);
|
|
54814
|
+
setProbes({});
|
|
53824
54815
|
setLoaded(true);
|
|
53825
54816
|
}).catch((e) => {
|
|
53826
54817
|
if (!cancelled) setError(e instanceof Error ? e.message : String(e));
|
|
@@ -53828,94 +54819,151 @@ function SettingsDrawer({ open, llmConfig, runtimeConfig, theme, locale, rendere
|
|
|
53828
54819
|
return () => {
|
|
53829
54820
|
cancelled = true;
|
|
53830
54821
|
};
|
|
53831
|
-
}, [
|
|
53832
|
-
|
|
53833
|
-
|
|
53834
|
-
|
|
53835
|
-
|
|
54822
|
+
}, [open, configStore]);
|
|
54823
|
+
useEffect(() => {
|
|
54824
|
+
if (!open) return;
|
|
54825
|
+
let cancelled = false;
|
|
54826
|
+
probeChromeBuiltinAvailability().then((result) => {
|
|
54827
|
+
if (!cancelled) setChrome(result);
|
|
54828
|
+
});
|
|
54829
|
+
return () => {
|
|
54830
|
+
cancelled = true;
|
|
54831
|
+
};
|
|
54832
|
+
}, [open]);
|
|
54833
|
+
useEffect(() => {
|
|
54834
|
+
if (!open) return;
|
|
54835
|
+
let cancelled = false;
|
|
54836
|
+
Promise.all(Object.keys(RENDERER_PROBES).map(async (kind) => [kind, await RENDERER_PROBES[kind]()])).then((results) => {
|
|
54837
|
+
if (!cancelled) setRendererPeers(Object.fromEntries(results));
|
|
54838
|
+
});
|
|
54839
|
+
return () => {
|
|
54840
|
+
cancelled = true;
|
|
54841
|
+
};
|
|
54842
|
+
}, [open]);
|
|
53836
54843
|
const fail = (e) => setError(e instanceof Error ? e.message : String(e));
|
|
53837
|
-
|
|
53838
|
-
|
|
53839
|
-
|
|
53840
|
-
|
|
53841
|
-
|
|
53842
|
-
|
|
53843
|
-
const parsed = temperature.trim() === "" ? void 0 : Number(temperature);
|
|
53844
|
-
if (parsed !== void 0 && Number.isFinite(parsed)) loop.temperature = parsed;
|
|
53845
|
-
const next = {
|
|
53846
|
-
...rc,
|
|
53847
|
-
streaming,
|
|
53848
|
-
loop,
|
|
53849
|
-
llm: {
|
|
53850
|
-
provider,
|
|
53851
|
-
model: model.trim(),
|
|
53852
|
-
...baseUrl.trim() ? { baseUrl: baseUrl.trim() } : {},
|
|
53853
|
-
...apiKey.trim() ? { apiKey: apiKey.trim() } : {}
|
|
53854
|
-
}
|
|
53855
|
-
};
|
|
53856
|
-
await configStore.save(next);
|
|
53857
|
-
onSaved();
|
|
53858
|
-
onClose();
|
|
53859
|
-
})().catch(fail);
|
|
53860
|
-
return;
|
|
53861
|
-
}
|
|
53862
|
-
llmConfig.save({
|
|
53863
|
-
...baseUrl.trim() ? { baseUrl: baseUrl.trim() } : {},
|
|
53864
|
-
...apiKey.trim() ? { apiKey: apiKey.trim() } : {},
|
|
53865
|
-
...model.trim() ? { model: model.trim() } : {}
|
|
53866
|
-
}).then(() => {
|
|
53867
|
-
onSaved();
|
|
53868
|
-
onClose();
|
|
53869
|
-
}).catch(fail);
|
|
53870
|
-
};
|
|
53871
|
-
const clear = () => {
|
|
53872
|
-
if (configStore) {
|
|
53873
|
-
(async () => {
|
|
53874
|
-
const rc = await configStore.load();
|
|
53875
|
-
const loop = { ...rc.loop };
|
|
53876
|
-
delete loop.temperature;
|
|
53877
|
-
const next = {
|
|
53878
|
-
...rc,
|
|
53879
|
-
streaming: true,
|
|
53880
|
-
loop,
|
|
53881
|
-
llm: {
|
|
53882
|
-
provider: "openai-compatible",
|
|
53883
|
-
model: ""
|
|
53884
|
-
}
|
|
53885
|
-
};
|
|
53886
|
-
await configStore.save(next);
|
|
53887
|
-
onSaved();
|
|
53888
|
-
onClose();
|
|
53889
|
-
})().catch(fail);
|
|
53890
|
-
return;
|
|
53891
|
-
}
|
|
53892
|
-
llmConfig.clear().then(() => {
|
|
54844
|
+
/** 所有写路径的唯一出口:读最新配置 → 打补丁 → 保存 → 通知宿主重装配 */
|
|
54845
|
+
const persist = (mutate) => {
|
|
54846
|
+
if (!configStore) return;
|
|
54847
|
+
(async () => {
|
|
54848
|
+
const rc = await configStore.load();
|
|
54849
|
+
await configStore.save(mutate(rc));
|
|
53893
54850
|
onSaved();
|
|
53894
|
-
|
|
53895
|
-
|
|
53896
|
-
|
|
53897
|
-
|
|
53898
|
-
|
|
53899
|
-
|
|
53900
|
-
|
|
53901
|
-
|
|
53902
|
-
|
|
53903
|
-
|
|
53904
|
-
|
|
53905
|
-
|
|
53906
|
-
|
|
54851
|
+
})().catch(fail);
|
|
54852
|
+
};
|
|
54853
|
+
const persistLlm = (next, nextDefaultId) => {
|
|
54854
|
+
setEntries(next);
|
|
54855
|
+
setDefaultId(nextDefaultId);
|
|
54856
|
+
persist((rc) => ({
|
|
54857
|
+
...rc,
|
|
54858
|
+
llm: {
|
|
54859
|
+
entries: next,
|
|
54860
|
+
...nextDefaultId !== void 0 ? { defaultId: nextDefaultId } : {}
|
|
54861
|
+
}
|
|
54862
|
+
}));
|
|
54863
|
+
};
|
|
54864
|
+
const chromeReason = chrome !== void 0 && !chrome.available ? t(`settings.chromeBuiltin.${chrome.reason}`) : void 0;
|
|
54865
|
+
const entryUnavailable = (entry) => entry.provider === "chrome-builtin" ? chromeReason : void 0;
|
|
54866
|
+
const saveEntry = () => {
|
|
54867
|
+
const provider = form.provider;
|
|
54868
|
+
const isBuiltin = provider === "chrome-builtin";
|
|
54869
|
+
const entry = {
|
|
54870
|
+
id: editing === "new" || editing === void 0 ? newEntryId() : editing,
|
|
54871
|
+
label: form.label.trim() !== "" ? form.label.trim() : form.model.trim() || t(`settings.provider.${provider}`),
|
|
54872
|
+
provider,
|
|
54873
|
+
model: form.model.trim(),
|
|
54874
|
+
...!isBuiltin && form.baseUrl.trim() !== "" ? { baseUrl: form.baseUrl.trim() } : {},
|
|
54875
|
+
...!isBuiltin && form.apiKey.trim() !== "" ? { apiKey: form.apiKey.trim() } : {}
|
|
54876
|
+
};
|
|
54877
|
+
const next = entries.some((item) => item.id === entry.id) ? entries.map((item) => item.id === entry.id ? entry : item) : [...entries, entry];
|
|
54878
|
+
setEditing(void 0);
|
|
54879
|
+
persistLlm(next, defaultId ?? entry.id);
|
|
54880
|
+
};
|
|
54881
|
+
const removeEntry = (id) => {
|
|
54882
|
+
const next = entries.filter((entry) => entry.id !== id);
|
|
54883
|
+
if (editing === id) setEditing(void 0);
|
|
54884
|
+
persistLlm(next, defaultId === id ? next[0]?.id : defaultId);
|
|
54885
|
+
};
|
|
54886
|
+
const probeEntry = (entry) => {
|
|
54887
|
+
const client = createLlmClient(entry);
|
|
54888
|
+
const streamingSupported = typeof client.stream === "function";
|
|
54889
|
+
setProbes((prev) => ({
|
|
54890
|
+
...prev,
|
|
54891
|
+
[entry.id]: { kind: "testing" }
|
|
54892
|
+
}));
|
|
54893
|
+
const done = (available) => setProbes((prev) => ({
|
|
54894
|
+
...prev,
|
|
54895
|
+
[entry.id]: {
|
|
53907
54896
|
kind: "done",
|
|
53908
54897
|
available,
|
|
53909
|
-
streaming:
|
|
53910
|
-
}
|
|
53911
|
-
})
|
|
53912
|
-
|
|
53913
|
-
|
|
53914
|
-
|
|
53915
|
-
|
|
53916
|
-
|
|
54898
|
+
streaming: streamingSupported
|
|
54899
|
+
}
|
|
54900
|
+
}));
|
|
54901
|
+
client.checkAvailability().then(done, () => done(false));
|
|
54902
|
+
};
|
|
54903
|
+
const setStreamingConfig = (next) => {
|
|
54904
|
+
setStreaming(next);
|
|
54905
|
+
persist((rc) => ({
|
|
54906
|
+
...rc,
|
|
54907
|
+
streaming: next
|
|
54908
|
+
}));
|
|
54909
|
+
};
|
|
54910
|
+
const commitTemperature = () => {
|
|
54911
|
+
const parsed = temperature.trim() === "" ? void 0 : Number(temperature);
|
|
54912
|
+
persist((rc) => {
|
|
54913
|
+
const loop = { ...rc.loop };
|
|
54914
|
+
delete loop.temperature;
|
|
54915
|
+
if (parsed !== void 0 && Number.isFinite(parsed)) loop.temperature = parsed;
|
|
54916
|
+
return {
|
|
54917
|
+
...rc,
|
|
54918
|
+
loop
|
|
54919
|
+
};
|
|
53917
54920
|
});
|
|
53918
54921
|
};
|
|
54922
|
+
const setAgentCapability = (patch) => persist((rc) => ({
|
|
54923
|
+
...rc,
|
|
54924
|
+
agentCapabilities: {
|
|
54925
|
+
...rc.agentCapabilities,
|
|
54926
|
+
...patch
|
|
54927
|
+
}
|
|
54928
|
+
}));
|
|
54929
|
+
/** 导出偏好(FR-5.1):凭据在 `exportPreferences` 里就进不来,这里只负责落盘 */
|
|
54930
|
+
const exportPrefs = () => {
|
|
54931
|
+
if (!configStore) return;
|
|
54932
|
+
(async () => {
|
|
54933
|
+
const payload = exportPreferences(await configStore.load(), { appearance: {
|
|
54934
|
+
theme,
|
|
54935
|
+
locale,
|
|
54936
|
+
...renderer ? { renderer } : {}
|
|
54937
|
+
} });
|
|
54938
|
+
if (onExportPreferences) onExportPreferences(payload);
|
|
54939
|
+
else downloadPreferences(payload);
|
|
54940
|
+
setPrefNotice(t("settings.preferences.exported"));
|
|
54941
|
+
})().catch(fail);
|
|
54942
|
+
};
|
|
54943
|
+
/** 导入偏好(FR-5.4):校验不过就整份拒,不做部分应用 */
|
|
54944
|
+
const importPrefs = (text) => {
|
|
54945
|
+
if (!configStore) return;
|
|
54946
|
+
(async () => {
|
|
54947
|
+
let parsed;
|
|
54948
|
+
try {
|
|
54949
|
+
parsed = JSON.parse(text);
|
|
54950
|
+
} catch {
|
|
54951
|
+
throw new Error("Preference file is not valid JSON");
|
|
54952
|
+
}
|
|
54953
|
+
const imported = importPreferences(parsed);
|
|
54954
|
+
const rc = await configStore.load();
|
|
54955
|
+
await configStore.save(applyPreferences(rc, imported));
|
|
54956
|
+
const appearance = imported.preferences.appearance;
|
|
54957
|
+
if (appearance) onAppearanceChange?.({
|
|
54958
|
+
...appearance.theme === "light" || appearance.theme === "dark" ? { theme: appearance.theme } : {},
|
|
54959
|
+
...appearance.locale === "en" || appearance.locale === "zh" ? { locale: appearance.locale } : {}
|
|
54960
|
+
});
|
|
54961
|
+
setPrefNotice(t("settings.preferences.imported"));
|
|
54962
|
+
onSaved();
|
|
54963
|
+
})().catch(fail);
|
|
54964
|
+
};
|
|
54965
|
+
const formBuiltin = form.provider === "chrome-builtin";
|
|
54966
|
+
const canProbeForm = formBuiltin || form.model.trim() !== "" && (form.apiKey.trim() !== "" || form.baseUrl.trim() !== "");
|
|
53919
54967
|
return /* @__PURE__ */ jsx(Sheet, {
|
|
53920
54968
|
open,
|
|
53921
54969
|
onOpenChange: (next) => !next && onClose(),
|
|
@@ -53928,194 +54976,487 @@ function SettingsDrawer({ open, llmConfig, runtimeConfig, theme, locale, rendere
|
|
|
53928
54976
|
children: t("settings.title")
|
|
53929
54977
|
}), /* @__PURE__ */ jsxs("div", {
|
|
53930
54978
|
className: "flex flex-col gap-6",
|
|
53931
|
-
children: [
|
|
53932
|
-
|
|
53933
|
-
|
|
53934
|
-
|
|
53935
|
-
|
|
53936
|
-
|
|
53937
|
-
|
|
53938
|
-
|
|
53939
|
-
|
|
53940
|
-
|
|
53941
|
-
|
|
53942
|
-
|
|
53943
|
-
|
|
53944
|
-
className: "text-xs text-muted",
|
|
53945
|
-
children: t("settings.demoNote")
|
|
53946
|
-
})]
|
|
53947
|
-
}) : null,
|
|
53948
|
-
error ? /* @__PURE__ */ jsx("div", {
|
|
53949
|
-
role: "alert",
|
|
53950
|
-
className: "rounded-lg border border-danger/30 bg-danger-soft px-3 py-2 text-xs text-danger",
|
|
53951
|
-
children: error
|
|
53952
|
-
}) : null,
|
|
53953
|
-
/* @__PURE__ */ jsx(FormField, {
|
|
53954
|
-
label: t("settings.provider"),
|
|
53955
|
-
description: t("settings.providerNote"),
|
|
53956
|
-
htmlFor: "webskill-settings-provider",
|
|
53957
|
-
children: /* @__PURE__ */ jsx(Select, {
|
|
53958
|
-
id: "webskill-settings-provider",
|
|
53959
|
-
className: "w-full",
|
|
53960
|
-
value: provider,
|
|
53961
|
-
disabled: !loaded || !runtimeConfig,
|
|
53962
|
-
options: PROVIDERS.map((value) => ({
|
|
53963
|
-
value,
|
|
53964
|
-
label: t(`settings.provider.${value}`)
|
|
53965
|
-
})),
|
|
53966
|
-
onChange: (value) => setProvider(value)
|
|
53967
|
-
})
|
|
53968
|
-
}),
|
|
53969
|
-
provider === "openai-compatible" ? /* @__PURE__ */ jsx(FormField, {
|
|
53970
|
-
label: t("settings.endpoint"),
|
|
53971
|
-
htmlFor: "webskill-settings-endpoint",
|
|
53972
|
-
children: /* @__PURE__ */ jsx(Input, {
|
|
53973
|
-
id: "webskill-settings-endpoint",
|
|
53974
|
-
value: baseUrl,
|
|
53975
|
-
placeholder: "https://api.example.com/v1",
|
|
53976
|
-
disabled: !loaded,
|
|
53977
|
-
onChange: (event) => setBaseUrl(event.target.value)
|
|
53978
|
-
})
|
|
53979
|
-
}) : /* @__PURE__ */ jsx("p", {
|
|
53980
|
-
className: "text-xs text-muted",
|
|
53981
|
-
children: t(`settings.provider.${provider}.note`)
|
|
53982
|
-
}),
|
|
53983
|
-
/* @__PURE__ */ jsx(FormField, {
|
|
53984
|
-
label: t("settings.apiKey"),
|
|
53985
|
-
htmlFor: "webskill-settings-apikey",
|
|
53986
|
-
children: /* @__PURE__ */ jsx(Input, {
|
|
53987
|
-
id: "webskill-settings-apikey",
|
|
53988
|
-
type: "password",
|
|
53989
|
-
value: apiKey,
|
|
53990
|
-
disabled: !loaded,
|
|
53991
|
-
onChange: (event) => setApiKey(event.target.value)
|
|
53992
|
-
})
|
|
53993
|
-
}),
|
|
53994
|
-
/* @__PURE__ */ jsx(FormField, {
|
|
53995
|
-
label: t("settings.modelName"),
|
|
53996
|
-
htmlFor: "webskill-settings-model",
|
|
53997
|
-
children: /* @__PURE__ */ jsx(Input, {
|
|
53998
|
-
id: "webskill-settings-model",
|
|
53999
|
-
value: model,
|
|
54000
|
-
placeholder: provider === "anthropic" ? "claude-sonnet-4-5" : provider === "google" ? "gemini-2.5-flash" : "gpt-4o-mini",
|
|
54001
|
-
disabled: !loaded,
|
|
54002
|
-
onChange: (event) => setModel(event.target.value)
|
|
54003
|
-
})
|
|
54004
|
-
}),
|
|
54005
|
-
runtimeConfig ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(FormField, {
|
|
54006
|
-
label: t("settings.streaming"),
|
|
54007
|
-
description: t("settings.streamingNote"),
|
|
54008
|
-
children: /* @__PURE__ */ jsx(Switch, {
|
|
54009
|
-
checked: streaming,
|
|
54010
|
-
disabled: !loaded,
|
|
54011
|
-
onCheckedChange: setStreaming
|
|
54012
|
-
})
|
|
54013
|
-
}), /* @__PURE__ */ jsx(FormField, {
|
|
54014
|
-
label: t("settings.temperature"),
|
|
54015
|
-
description: t("settings.temperatureNote"),
|
|
54016
|
-
htmlFor: "webskill-settings-temperature",
|
|
54017
|
-
children: /* @__PURE__ */ jsx(Input, {
|
|
54018
|
-
id: "webskill-settings-temperature",
|
|
54019
|
-
type: "number",
|
|
54020
|
-
value: temperature,
|
|
54021
|
-
placeholder: "1.0",
|
|
54022
|
-
disabled: !loaded,
|
|
54023
|
-
onChange: (event) => setTemperature(event.target.value)
|
|
54024
|
-
})
|
|
54025
|
-
})] }) : null,
|
|
54026
|
-
/* @__PURE__ */ jsxs("div", {
|
|
54027
|
-
className: "flex flex-col gap-2",
|
|
54028
|
-
children: [/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, {
|
|
54029
|
-
variant: "secondary",
|
|
54030
|
-
size: "sm",
|
|
54031
|
-
icon: /* @__PURE__ */ jsx(PlugZap, {
|
|
54032
|
-
className: "size-3.5",
|
|
54033
|
-
"aria-hidden": true
|
|
54034
|
-
}),
|
|
54035
|
-
loading: probe.kind === "testing",
|
|
54036
|
-
disabled: !canProbe,
|
|
54037
|
-
onClick: testConnection,
|
|
54038
|
-
children: probe.kind === "testing" ? t("settings.testing") : t("settings.test")
|
|
54039
|
-
}) }), probe.kind === "done" ? /* @__PURE__ */ jsxs("div", {
|
|
54040
|
-
className: "flex flex-col gap-1.5",
|
|
54041
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
54042
|
-
className: "flex flex-wrap items-center gap-2",
|
|
54043
|
-
children: [/* @__PURE__ */ jsx(Badge, {
|
|
54044
|
-
tone: probe.available ? "success" : "danger",
|
|
54045
|
-
children: probe.available ? t("settings.available") : t("settings.unavailable")
|
|
54046
|
-
}), probe.available && probe.streaming ? /* @__PURE__ */ jsx(Badge, {
|
|
54047
|
-
tone: "neutral",
|
|
54048
|
-
children: t("settings.streamingSupported")
|
|
54049
|
-
}) : null]
|
|
54979
|
+
children: [
|
|
54980
|
+
/* @__PURE__ */ jsxs("section", {
|
|
54981
|
+
className: "flex flex-col gap-4",
|
|
54982
|
+
children: [
|
|
54983
|
+
/* @__PURE__ */ jsx(DescriptionText, {
|
|
54984
|
+
title: t("settings.model.title"),
|
|
54985
|
+
description: t("settings.model.description")
|
|
54986
|
+
}),
|
|
54987
|
+
loaded && entries.length === 0 ? /* @__PURE__ */ jsxs("div", {
|
|
54988
|
+
className: "flex items-start gap-2 rounded-card border border-warning/30 bg-warning-soft px-3 py-2.5",
|
|
54989
|
+
children: [/* @__PURE__ */ jsx(Badge, {
|
|
54990
|
+
tone: "warning",
|
|
54991
|
+
children: t("settings.demoBadge")
|
|
54050
54992
|
}), /* @__PURE__ */ jsx("p", {
|
|
54051
54993
|
className: "text-xs text-muted",
|
|
54052
|
-
children: t("settings.
|
|
54994
|
+
children: t("settings.demoNote")
|
|
54053
54995
|
})]
|
|
54054
|
-
}) : null
|
|
54055
|
-
|
|
54056
|
-
|
|
54057
|
-
|
|
54058
|
-
|
|
54059
|
-
|
|
54060
|
-
|
|
54061
|
-
|
|
54996
|
+
}) : null,
|
|
54997
|
+
error ? /* @__PURE__ */ jsx("div", {
|
|
54998
|
+
role: "alert",
|
|
54999
|
+
className: "rounded-lg border border-danger/30 bg-danger-soft px-3 py-2 text-xs text-danger",
|
|
55000
|
+
children: error
|
|
55001
|
+
}) : null,
|
|
55002
|
+
/* @__PURE__ */ jsx("ul", {
|
|
55003
|
+
className: "flex flex-col gap-2",
|
|
55004
|
+
"data-testid": "settings-model-list",
|
|
55005
|
+
children: entries.map((entry) => {
|
|
55006
|
+
const unavailable = entryUnavailable(entry);
|
|
55007
|
+
const probe = probes[entry.id];
|
|
55008
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
55009
|
+
"data-testid": "settings-model-row",
|
|
55010
|
+
"data-entry-id": entry.id,
|
|
55011
|
+
className: "flex flex-col gap-1.5 rounded-card border border-border px-3 py-2.5",
|
|
55012
|
+
children: [
|
|
55013
|
+
/* @__PURE__ */ jsxs("div", {
|
|
55014
|
+
className: "flex flex-wrap items-center gap-2",
|
|
55015
|
+
children: [
|
|
55016
|
+
/* @__PURE__ */ jsx("span", {
|
|
55017
|
+
className: "text-sm font-medium text-ink",
|
|
55018
|
+
children: entry.label
|
|
55019
|
+
}),
|
|
55020
|
+
entry.id === defaultId ? /* @__PURE__ */ jsx(Badge, {
|
|
55021
|
+
tone: "success",
|
|
55022
|
+
children: t("settings.model.default")
|
|
55023
|
+
}) : null,
|
|
55024
|
+
unavailable ? /* @__PURE__ */ jsx(Badge, {
|
|
55025
|
+
tone: "warning",
|
|
55026
|
+
children: t("settings.model.unavailable")
|
|
55027
|
+
}) : null
|
|
55028
|
+
]
|
|
55029
|
+
}),
|
|
55030
|
+
/* @__PURE__ */ jsxs("p", {
|
|
55031
|
+
className: "text-xs text-muted",
|
|
55032
|
+
children: [t(`settings.provider.${entry.provider}`), entry.model.trim() !== "" ? ` · ${entry.model}` : ""]
|
|
55033
|
+
}),
|
|
55034
|
+
unavailable ? /* @__PURE__ */ jsx("p", {
|
|
55035
|
+
className: "text-xs text-warning",
|
|
55036
|
+
children: unavailable
|
|
55037
|
+
}) : null,
|
|
55038
|
+
entry.provider === "chrome-builtin" ? /* @__PURE__ */ jsx("p", {
|
|
55039
|
+
className: "text-xs text-muted",
|
|
55040
|
+
children: t("settings.provider.chrome-builtin.limitation")
|
|
55041
|
+
}) : null,
|
|
55042
|
+
probe?.kind === "done" ? /* @__PURE__ */ jsxs("div", {
|
|
55043
|
+
className: "flex flex-wrap items-center gap-2",
|
|
55044
|
+
children: [/* @__PURE__ */ jsx(Badge, {
|
|
55045
|
+
tone: probe.available ? "success" : "danger",
|
|
55046
|
+
children: probe.available ? t("settings.available") : t("settings.unavailable")
|
|
55047
|
+
}), probe.available && probe.streaming ? /* @__PURE__ */ jsx(Badge, {
|
|
55048
|
+
tone: "neutral",
|
|
55049
|
+
children: t("settings.streamingSupported")
|
|
55050
|
+
}) : null]
|
|
55051
|
+
}) : null,
|
|
55052
|
+
/* @__PURE__ */ jsxs("div", {
|
|
55053
|
+
className: "flex flex-wrap items-center gap-2 pt-0.5",
|
|
55054
|
+
children: [
|
|
55055
|
+
/* @__PURE__ */ jsx(Button, {
|
|
55056
|
+
variant: "secondary",
|
|
55057
|
+
size: "sm",
|
|
55058
|
+
icon: /* @__PURE__ */ jsx(PlugZap, {
|
|
55059
|
+
className: "size-3.5",
|
|
55060
|
+
"aria-hidden": true
|
|
55061
|
+
}),
|
|
55062
|
+
loading: probe?.kind === "testing",
|
|
55063
|
+
onClick: () => probeEntry(entry),
|
|
55064
|
+
children: probe?.kind === "testing" ? t("settings.testing") : t("settings.test")
|
|
55065
|
+
}),
|
|
55066
|
+
/* @__PURE__ */ jsx(Button, {
|
|
55067
|
+
variant: "ghost",
|
|
55068
|
+
size: "sm",
|
|
55069
|
+
onClick: () => {
|
|
55070
|
+
setForm(toForm(entry));
|
|
55071
|
+
setEditing(entry.id);
|
|
55072
|
+
},
|
|
55073
|
+
children: t("settings.model.edit")
|
|
55074
|
+
}),
|
|
55075
|
+
entry.id === defaultId ? null : /* @__PURE__ */ jsx(Button, {
|
|
55076
|
+
variant: "ghost",
|
|
55077
|
+
size: "sm",
|
|
55078
|
+
onClick: () => persistLlm(entries, entry.id),
|
|
55079
|
+
children: t("settings.model.setDefault")
|
|
55080
|
+
}),
|
|
55081
|
+
/* @__PURE__ */ jsx(Button, {
|
|
55082
|
+
variant: "ghost",
|
|
55083
|
+
size: "sm",
|
|
55084
|
+
onClick: () => removeEntry(entry.id),
|
|
55085
|
+
children: t("settings.model.remove")
|
|
55086
|
+
})
|
|
55087
|
+
]
|
|
55088
|
+
})
|
|
55089
|
+
]
|
|
55090
|
+
}, entry.id);
|
|
55091
|
+
})
|
|
55092
|
+
}),
|
|
55093
|
+
loaded && entries.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
55094
|
+
className: "text-xs text-muted",
|
|
55095
|
+
children: t("settings.model.empty")
|
|
55096
|
+
}) : null,
|
|
55097
|
+
editing === void 0 ? /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, {
|
|
54062
55098
|
variant: "secondary",
|
|
54063
|
-
|
|
54064
|
-
|
|
54065
|
-
|
|
54066
|
-
|
|
54067
|
-
|
|
54068
|
-
|
|
54069
|
-
|
|
54070
|
-
|
|
54071
|
-
|
|
54072
|
-
|
|
54073
|
-
|
|
54074
|
-
|
|
54075
|
-
|
|
54076
|
-
|
|
54077
|
-
|
|
54078
|
-
|
|
54079
|
-
|
|
54080
|
-
|
|
54081
|
-
|
|
54082
|
-
|
|
54083
|
-
|
|
54084
|
-
|
|
54085
|
-
|
|
54086
|
-
|
|
54087
|
-
|
|
54088
|
-
|
|
54089
|
-
|
|
55099
|
+
size: "sm",
|
|
55100
|
+
"data-testid": "settings-model-add",
|
|
55101
|
+
disabled: !loaded,
|
|
55102
|
+
onClick: () => {
|
|
55103
|
+
setForm(EMPTY_FORM);
|
|
55104
|
+
setEditing("new");
|
|
55105
|
+
},
|
|
55106
|
+
children: t("settings.model.add")
|
|
55107
|
+
}) }) : /* @__PURE__ */ jsxs("div", {
|
|
55108
|
+
className: "flex flex-col gap-4 rounded-card border border-border p-3",
|
|
55109
|
+
"data-testid": "settings-model-form",
|
|
55110
|
+
children: [
|
|
55111
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55112
|
+
label: t("settings.model.label"),
|
|
55113
|
+
description: t("settings.model.labelNote"),
|
|
55114
|
+
htmlFor: "webskill-settings-label",
|
|
55115
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
55116
|
+
id: "webskill-settings-label",
|
|
55117
|
+
value: form.label,
|
|
55118
|
+
onChange: (event) => setForm({
|
|
55119
|
+
...form,
|
|
55120
|
+
label: event.target.value
|
|
55121
|
+
})
|
|
55122
|
+
})
|
|
55123
|
+
}),
|
|
55124
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55125
|
+
label: t("settings.provider"),
|
|
55126
|
+
description: t("settings.providerNote"),
|
|
55127
|
+
htmlFor: "webskill-settings-provider",
|
|
55128
|
+
children: /* @__PURE__ */ jsx(Select, {
|
|
55129
|
+
id: "webskill-settings-provider",
|
|
55130
|
+
className: "w-full",
|
|
55131
|
+
value: form.provider,
|
|
55132
|
+
options: PROVIDERS.map((value) => ({
|
|
55133
|
+
value,
|
|
55134
|
+
label: t(`settings.provider.${value}`),
|
|
55135
|
+
...value === "chrome-builtin" && chromeReason !== void 0 ? { disabled: true } : {}
|
|
55136
|
+
})),
|
|
55137
|
+
onChange: (value) => setForm({
|
|
55138
|
+
...form,
|
|
55139
|
+
provider: value
|
|
55140
|
+
})
|
|
55141
|
+
})
|
|
55142
|
+
}),
|
|
55143
|
+
chromeReason !== void 0 ? /* @__PURE__ */ jsx("p", {
|
|
55144
|
+
className: "text-xs text-warning",
|
|
55145
|
+
"data-testid": "settings-chrome-unavailable",
|
|
55146
|
+
children: chromeReason
|
|
55147
|
+
}) : null,
|
|
55148
|
+
formBuiltin ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
|
|
55149
|
+
className: "text-xs text-muted",
|
|
55150
|
+
children: t("settings.provider.chrome-builtin.note")
|
|
55151
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
55152
|
+
className: "text-xs text-warning",
|
|
55153
|
+
children: t("settings.provider.chrome-builtin.limitation")
|
|
55154
|
+
})] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
55155
|
+
form.provider === "openai-compatible" ? /* @__PURE__ */ jsx(FormField, {
|
|
55156
|
+
label: t("settings.endpoint"),
|
|
55157
|
+
htmlFor: "webskill-settings-endpoint",
|
|
55158
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
55159
|
+
id: "webskill-settings-endpoint",
|
|
55160
|
+
value: form.baseUrl,
|
|
55161
|
+
placeholder: "https://api.example.com/v1",
|
|
55162
|
+
onChange: (event) => setForm({
|
|
55163
|
+
...form,
|
|
55164
|
+
baseUrl: event.target.value
|
|
55165
|
+
})
|
|
55166
|
+
})
|
|
55167
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
55168
|
+
className: "text-xs text-muted",
|
|
55169
|
+
children: t(`settings.provider.${form.provider}.note`)
|
|
55170
|
+
}),
|
|
55171
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55172
|
+
label: t("settings.apiKey"),
|
|
55173
|
+
htmlFor: "webskill-settings-apikey",
|
|
55174
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
55175
|
+
id: "webskill-settings-apikey",
|
|
55176
|
+
type: "password",
|
|
55177
|
+
value: form.apiKey,
|
|
55178
|
+
onChange: (event) => setForm({
|
|
55179
|
+
...form,
|
|
55180
|
+
apiKey: event.target.value
|
|
55181
|
+
})
|
|
55182
|
+
})
|
|
55183
|
+
}),
|
|
55184
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55185
|
+
label: t("settings.modelName"),
|
|
55186
|
+
htmlFor: "webskill-settings-model",
|
|
55187
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
55188
|
+
id: "webskill-settings-model",
|
|
55189
|
+
value: form.model,
|
|
55190
|
+
placeholder: form.provider === "anthropic" ? "claude-sonnet-4-5" : form.provider === "google" ? "gemini-2.5-flash" : "gpt-4o-mini",
|
|
55191
|
+
onChange: (event) => setForm({
|
|
55192
|
+
...form,
|
|
55193
|
+
model: event.target.value
|
|
55194
|
+
})
|
|
55195
|
+
})
|
|
55196
|
+
})
|
|
55197
|
+
] }),
|
|
55198
|
+
/* @__PURE__ */ jsxs("div", {
|
|
55199
|
+
className: "flex items-center gap-2",
|
|
55200
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
55201
|
+
"data-testid": "settings-model-save",
|
|
55202
|
+
disabled: !canProbeForm,
|
|
55203
|
+
onClick: saveEntry,
|
|
55204
|
+
children: t("settings.save")
|
|
55205
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
55206
|
+
variant: "secondary",
|
|
55207
|
+
onClick: () => setEditing(void 0),
|
|
55208
|
+
children: t("settings.model.cancel")
|
|
55209
|
+
})]
|
|
55210
|
+
})
|
|
55211
|
+
]
|
|
55212
|
+
}),
|
|
55213
|
+
/* @__PURE__ */ jsx("p", {
|
|
55214
|
+
className: "text-xs text-muted",
|
|
55215
|
+
children: t("settings.nonStreamingFallback")
|
|
55216
|
+
}),
|
|
55217
|
+
runtimeConfig ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(FormField, {
|
|
55218
|
+
label: t("settings.streaming"),
|
|
55219
|
+
description: t("settings.streamingNote"),
|
|
55220
|
+
children: /* @__PURE__ */ jsx(Switch, {
|
|
55221
|
+
checked: streaming,
|
|
55222
|
+
disabled: !loaded,
|
|
55223
|
+
onCheckedChange: setStreamingConfig
|
|
55224
|
+
})
|
|
55225
|
+
}), /* @__PURE__ */ jsx(FormField, {
|
|
55226
|
+
label: t("settings.temperature"),
|
|
55227
|
+
description: t("settings.temperatureNote"),
|
|
55228
|
+
htmlFor: "webskill-settings-temperature",
|
|
55229
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
55230
|
+
id: "webskill-settings-temperature",
|
|
55231
|
+
type: "number",
|
|
55232
|
+
value: temperature,
|
|
55233
|
+
placeholder: "1.0",
|
|
55234
|
+
disabled: !loaded,
|
|
55235
|
+
onChange: (event) => setTemperature(event.target.value),
|
|
55236
|
+
onBlur: commitTemperature
|
|
55237
|
+
})
|
|
55238
|
+
})] }) : null
|
|
55239
|
+
]
|
|
55240
|
+
}),
|
|
55241
|
+
runtimeConfig ? /* @__PURE__ */ jsxs("section", {
|
|
55242
|
+
className: "flex flex-col gap-4 border-t border-border pt-5",
|
|
55243
|
+
children: [
|
|
55244
|
+
/* @__PURE__ */ jsx(DescriptionText, {
|
|
55245
|
+
title: t("settings.agent.title"),
|
|
55246
|
+
description: t("settings.agent.description")
|
|
55247
|
+
}),
|
|
55248
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55249
|
+
label: t("settings.agent.todo"),
|
|
55250
|
+
description: t("settings.agent.todoNote"),
|
|
55251
|
+
children: /* @__PURE__ */ jsx(Switch, {
|
|
55252
|
+
"data-testid": "settings-agent-todo",
|
|
55253
|
+
checked: todoEnabled,
|
|
55254
|
+
disabled: !loaded,
|
|
55255
|
+
onCheckedChange: (next) => {
|
|
55256
|
+
setTodoEnabled(next);
|
|
55257
|
+
setAgentCapability({ todo: next });
|
|
55258
|
+
}
|
|
55259
|
+
})
|
|
55260
|
+
}),
|
|
55261
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55262
|
+
label: t("settings.agent.generativeUi"),
|
|
55263
|
+
description: t("settings.agent.generativeUiNote"),
|
|
55264
|
+
children: /* @__PURE__ */ jsx(Switch, {
|
|
55265
|
+
"data-testid": "settings-agent-generative-ui",
|
|
55266
|
+
checked: generativeUiEnabled,
|
|
55267
|
+
disabled: !loaded,
|
|
55268
|
+
onCheckedChange: (next) => {
|
|
55269
|
+
setGenerativeUiEnabled(next);
|
|
55270
|
+
setAgentCapability({ generativeUi: next });
|
|
55271
|
+
}
|
|
55272
|
+
})
|
|
55273
|
+
}),
|
|
55274
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55275
|
+
label: t("settings.agent.skillGeneration"),
|
|
55276
|
+
description: t("settings.agent.skillGenerationNote"),
|
|
55277
|
+
children: /* @__PURE__ */ jsx(Switch, {
|
|
55278
|
+
"data-testid": "settings-agent-skill-generation",
|
|
55279
|
+
checked: skillGenerationEnabled,
|
|
55280
|
+
disabled: !loaded,
|
|
55281
|
+
onCheckedChange: (next) => {
|
|
55282
|
+
setSkillGenerationEnabled(next);
|
|
55283
|
+
setAgentCapability({ skillGeneration: next });
|
|
55284
|
+
}
|
|
55285
|
+
})
|
|
55286
|
+
}),
|
|
55287
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55288
|
+
label: t("settings.agent.delegation"),
|
|
55289
|
+
description: t("settings.agent.delegationNote"),
|
|
55290
|
+
children: /* @__PURE__ */ jsx(Switch, {
|
|
55291
|
+
"data-testid": "settings-agent-delegation",
|
|
55292
|
+
checked: delegationEnabled,
|
|
55293
|
+
disabled: !loaded,
|
|
55294
|
+
onCheckedChange: (next) => {
|
|
55295
|
+
setDelegationEnabled(next);
|
|
55296
|
+
setAgentCapability({ delegation: next });
|
|
55297
|
+
}
|
|
55298
|
+
})
|
|
55299
|
+
}),
|
|
55300
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55301
|
+
label: t("settings.agent.formAutofill"),
|
|
55302
|
+
description: t("settings.agent.formAutofillNote"),
|
|
55303
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
55304
|
+
className: "flex items-center gap-3",
|
|
55305
|
+
children: [/* @__PURE__ */ jsx(Switch, {
|
|
55306
|
+
"data-testid": "settings-agent-form-autofill",
|
|
55307
|
+
checked: formAutofillEnabled,
|
|
55308
|
+
disabled: !loaded,
|
|
55309
|
+
onCheckedChange: (next) => {
|
|
55310
|
+
setFormAutofillEnabled(next);
|
|
55311
|
+
setAgentCapability({ formAutofill: next });
|
|
55312
|
+
}
|
|
55313
|
+
}), onClearFormAutofill ? /* @__PURE__ */ jsx(Button, {
|
|
55314
|
+
"data-testid": "settings-agent-form-autofill-clear",
|
|
55315
|
+
variant: "ghost",
|
|
55316
|
+
size: "sm",
|
|
55317
|
+
onClick: () => {
|
|
55318
|
+
onClearFormAutofill().then(() => setAutofillCleared(true)).catch(fail);
|
|
55319
|
+
},
|
|
55320
|
+
children: autofillCleared ? t("settings.agent.formAutofillCleared") : t("settings.agent.formAutofillClear")
|
|
55321
|
+
}) : null]
|
|
55322
|
+
})
|
|
54090
55323
|
})
|
|
54091
|
-
|
|
54092
|
-
|
|
54093
|
-
|
|
54094
|
-
|
|
55324
|
+
]
|
|
55325
|
+
}) : null,
|
|
55326
|
+
/* @__PURE__ */ jsxs("section", {
|
|
55327
|
+
className: "flex flex-col gap-4 border-t border-border pt-5",
|
|
55328
|
+
children: [
|
|
55329
|
+
/* @__PURE__ */ jsx(DescriptionText, {
|
|
55330
|
+
title: t("settings.appearance.title"),
|
|
55331
|
+
description: t("settings.appearance.description")
|
|
55332
|
+
}),
|
|
55333
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55334
|
+
label: t("settings.theme"),
|
|
55335
|
+
htmlFor: "webskill-settings-theme",
|
|
55336
|
+
children: /* @__PURE__ */ jsx(Select, {
|
|
55337
|
+
id: "webskill-settings-theme",
|
|
55338
|
+
className: "w-full",
|
|
55339
|
+
value: theme,
|
|
55340
|
+
options: [{
|
|
55341
|
+
value: "light",
|
|
55342
|
+
label: t("settings.theme.light")
|
|
55343
|
+
}, {
|
|
55344
|
+
value: "dark",
|
|
55345
|
+
label: t("settings.theme.dark")
|
|
55346
|
+
}],
|
|
55347
|
+
onChange: (value) => onAppearanceChange?.({ theme: value })
|
|
55348
|
+
})
|
|
55349
|
+
}),
|
|
55350
|
+
/* @__PURE__ */ jsx(FormField, {
|
|
55351
|
+
label: t("settings.language"),
|
|
55352
|
+
htmlFor: "webskill-settings-locale",
|
|
55353
|
+
children: /* @__PURE__ */ jsx(Select, {
|
|
55354
|
+
id: "webskill-settings-locale",
|
|
55355
|
+
className: "w-full",
|
|
55356
|
+
value: locale,
|
|
55357
|
+
options: [{
|
|
55358
|
+
value: "en",
|
|
55359
|
+
label: t("settings.language.en")
|
|
55360
|
+
}, {
|
|
55361
|
+
value: "zh",
|
|
55362
|
+
label: t("settings.language.zh")
|
|
55363
|
+
}],
|
|
55364
|
+
onChange: (value) => onAppearanceChange?.({ locale: value })
|
|
55365
|
+
})
|
|
55366
|
+
}),
|
|
55367
|
+
renderer !== void 0 ? /* @__PURE__ */ jsx(FormField, {
|
|
55368
|
+
label: t("settings.renderer"),
|
|
55369
|
+
description: t("settings.renderer.description"),
|
|
55370
|
+
htmlFor: "webskill-settings-renderer",
|
|
55371
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
55372
|
+
className: "flex flex-col gap-1.5",
|
|
55373
|
+
children: [/* @__PURE__ */ jsx(Select, {
|
|
55374
|
+
id: "webskill-settings-renderer",
|
|
55375
|
+
className: "w-full",
|
|
55376
|
+
value: renderer,
|
|
55377
|
+
options: RENDERERS.map((kind) => ({
|
|
55378
|
+
value: kind,
|
|
55379
|
+
label: t(`settings.renderer.${kind}`),
|
|
55380
|
+
...kind !== "native" && rendererPeers[kind] === false ? { disabled: true } : {}
|
|
55381
|
+
})),
|
|
55382
|
+
onChange: (value) => onAppearanceChange?.({ renderer: value })
|
|
55383
|
+
}), RENDERERS.filter((kind) => kind !== "native" && rendererPeers[kind] === false).map((kind) => /* @__PURE__ */ jsxs("p", {
|
|
55384
|
+
"data-testid": "settings-renderer-unavailable",
|
|
55385
|
+
className: "text-xs text-muted",
|
|
55386
|
+
children: [
|
|
55387
|
+
t(`settings.renderer.${kind}`),
|
|
55388
|
+
": ",
|
|
55389
|
+
MISSING_PEER[kind]
|
|
55390
|
+
]
|
|
55391
|
+
}, kind))]
|
|
55392
|
+
})
|
|
55393
|
+
}) : null
|
|
55394
|
+
]
|
|
55395
|
+
}),
|
|
55396
|
+
/* @__PURE__ */ jsxs("section", {
|
|
55397
|
+
className: "flex flex-col gap-3 border-t border-border pt-5",
|
|
55398
|
+
children: [/* @__PURE__ */ jsx(DescriptionText, {
|
|
55399
|
+
title: t("settings.voice.title"),
|
|
55400
|
+
description: t("settings.voice.description")
|
|
55401
|
+
}), /* @__PURE__ */ jsx(FormField, {
|
|
55402
|
+
label: t("settings.voice.lang"),
|
|
55403
|
+
htmlFor: "webskill-settings-dictation-lang",
|
|
54095
55404
|
children: /* @__PURE__ */ jsx(Select, {
|
|
54096
|
-
id: "webskill-settings-
|
|
55405
|
+
id: "webskill-settings-dictation-lang",
|
|
54097
55406
|
className: "w-full",
|
|
54098
|
-
value:
|
|
55407
|
+
value: dictationLang,
|
|
54099
55408
|
options: [{
|
|
54100
|
-
value: "
|
|
54101
|
-
label: t("settings.
|
|
54102
|
-
}, {
|
|
54103
|
-
value:
|
|
54104
|
-
label:
|
|
54105
|
-
}],
|
|
54106
|
-
onChange: (value) => onAppearanceChange?.({
|
|
55409
|
+
value: "",
|
|
55410
|
+
label: t("settings.voice.langFollow")
|
|
55411
|
+
}, ...DICTATION_LANGS.map((lang) => ({
|
|
55412
|
+
value: lang,
|
|
55413
|
+
label: lang
|
|
55414
|
+
}))],
|
|
55415
|
+
onChange: (value) => onAppearanceChange?.({ dictationLang: value })
|
|
54107
55416
|
})
|
|
54108
|
-
})
|
|
54109
|
-
|
|
54110
|
-
|
|
54111
|
-
|
|
54112
|
-
|
|
54113
|
-
|
|
54114
|
-
|
|
54115
|
-
|
|
54116
|
-
|
|
54117
|
-
|
|
54118
|
-
|
|
55417
|
+
})]
|
|
55418
|
+
}),
|
|
55419
|
+
runtimeConfig ? /* @__PURE__ */ jsxs("section", {
|
|
55420
|
+
className: "flex flex-col gap-3 border-t border-border pt-5",
|
|
55421
|
+
children: [
|
|
55422
|
+
/* @__PURE__ */ jsx(DescriptionText, {
|
|
55423
|
+
title: t("settings.preferences.title"),
|
|
55424
|
+
description: t("settings.preferences.description")
|
|
55425
|
+
}),
|
|
55426
|
+
/* @__PURE__ */ jsxs("div", {
|
|
55427
|
+
className: "flex items-center gap-2",
|
|
55428
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
55429
|
+
"data-testid": "settings-preferences-export",
|
|
55430
|
+
variant: "secondary",
|
|
55431
|
+
size: "sm",
|
|
55432
|
+
onClick: exportPrefs,
|
|
55433
|
+
children: t("settings.preferences.export")
|
|
55434
|
+
}), /* @__PURE__ */ jsxs("label", {
|
|
55435
|
+
className: "inline-flex",
|
|
55436
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
55437
|
+
className: "sr-only",
|
|
55438
|
+
children: t("settings.preferences.import")
|
|
55439
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
55440
|
+
"data-testid": "settings-preferences-import",
|
|
55441
|
+
type: "file",
|
|
55442
|
+
accept: "application/json,.json",
|
|
55443
|
+
className: "text-xs",
|
|
55444
|
+
onChange: (event) => {
|
|
55445
|
+
const file = event.target.files?.[0];
|
|
55446
|
+
event.target.value = "";
|
|
55447
|
+
if (file) file.text().then(importPrefs, fail);
|
|
55448
|
+
}
|
|
55449
|
+
})]
|
|
55450
|
+
})]
|
|
55451
|
+
}),
|
|
55452
|
+
prefNotice ? /* @__PURE__ */ jsx("p", {
|
|
55453
|
+
"data-testid": "settings-preferences-notice",
|
|
55454
|
+
className: "text-xs text-muted",
|
|
55455
|
+
children: prefNotice
|
|
55456
|
+
}) : null
|
|
55457
|
+
]
|
|
55458
|
+
}) : null
|
|
55459
|
+
]
|
|
54119
55460
|
})]
|
|
54120
55461
|
})
|
|
54121
55462
|
});
|
|
@@ -54176,7 +55517,7 @@ async function downloadArtifactFile(storage, chatRoot, file) {
|
|
|
54176
55517
|
* presentation shell and accessible thread primitives.
|
|
54177
55518
|
* @stable
|
|
54178
55519
|
*/
|
|
54179
|
-
function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, surfaceRegistry, layout = "auto", onAppearanceChange, onEngineReady }) {
|
|
55520
|
+
function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, dictationLang: dictationLangProp, surfaceRegistry, layout = "auto", onAppearanceChange, onEngineReady }) {
|
|
54180
55521
|
const engine = useMemo(() => new ChatEngine(adapter, {
|
|
54181
55522
|
...config?.chatRoot ? { chatRoot: config.chatRoot } : {},
|
|
54182
55523
|
...config?.llm ? { llm: config.llm } : {},
|
|
@@ -54185,6 +55526,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54185
55526
|
...config?.generativeUi ? { generativeUi: true } : {},
|
|
54186
55527
|
...config?.hooks ? { hooks: config.hooks } : {},
|
|
54187
55528
|
...config?.governance ? { governance: config.governance } : {},
|
|
55529
|
+
...config?.skillCandidates ? { skillCandidates: config.skillCandidates } : {},
|
|
54188
55530
|
...rendererProp ? { renderer: rendererProp } : {}
|
|
54189
55531
|
}), [
|
|
54190
55532
|
adapter,
|
|
@@ -54195,6 +55537,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54195
55537
|
config?.generativeUi,
|
|
54196
55538
|
config?.hooks,
|
|
54197
55539
|
config?.governance,
|
|
55540
|
+
config?.skillCandidates,
|
|
54198
55541
|
rendererProp
|
|
54199
55542
|
]);
|
|
54200
55543
|
useEffect(() => {
|
|
@@ -54203,6 +55546,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54203
55546
|
const [theme, setTheme] = useState(themeProp ?? "light");
|
|
54204
55547
|
const [locale, setLocale] = useState(localeProp ?? "en");
|
|
54205
55548
|
const [renderer, setRenderer] = useState(rendererProp ?? "native");
|
|
55549
|
+
/** 空串 = 跟随界面语言,由 composer 自己映射成 BCP-47 */
|
|
55550
|
+
const [dictationLang, setDictationLang] = useState(dictationLangProp ?? "");
|
|
54206
55551
|
useEffect(() => {
|
|
54207
55552
|
if (themeProp !== void 0) setTheme(themeProp);
|
|
54208
55553
|
}, [themeProp]);
|
|
@@ -54212,6 +55557,9 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54212
55557
|
useEffect(() => {
|
|
54213
55558
|
if (rendererProp !== void 0) setRenderer(rendererProp);
|
|
54214
55559
|
}, [rendererProp]);
|
|
55560
|
+
useEffect(() => {
|
|
55561
|
+
if (dictationLangProp !== void 0) setDictationLang(dictationLangProp);
|
|
55562
|
+
}, [dictationLangProp]);
|
|
54215
55563
|
useEffect(() => {
|
|
54216
55564
|
engine.setRenderer(renderer);
|
|
54217
55565
|
}, [engine, renderer]);
|
|
@@ -54236,6 +55584,14 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54236
55584
|
const [interactionPending, setInteractionPending] = useState(false);
|
|
54237
55585
|
/** pending 交互归属的会话(跨会话查看时隐藏交互卡,防串台) */
|
|
54238
55586
|
const [interactionSessionId, setInteractionSessionId] = useState();
|
|
55587
|
+
/** 会话级模型选择(需求 4):composer 选择器的受控值,权威值在 engine 里 */
|
|
55588
|
+
const [modelId, setModelId] = useState();
|
|
55589
|
+
/** RuntimeConfig.llm 的镜像(选择器选项来源;设置抽屉保存后重取) */
|
|
55590
|
+
const [llmSelection, setLlmSelection] = useState({ entries: [] });
|
|
55591
|
+
/** 设置抽屉每次保存后 +1,触发模型列表重取 */
|
|
55592
|
+
const [configVersion, setConfigVersion] = useState(0);
|
|
55593
|
+
/** Chrome 内置模型可用性(不可用的条目在选择器里置灰,AC-4.6) */
|
|
55594
|
+
const [chromeBuiltin, setChromeBuiltin] = useState();
|
|
54239
55595
|
/** currentSessionId 的 ref 镜像(事件订阅闭包 [engine] 内读取最新值) */
|
|
54240
55596
|
const sessionRef = useRef(currentSessionId);
|
|
54241
55597
|
sessionRef.current = currentSessionId;
|
|
@@ -54260,6 +55616,49 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54260
55616
|
cancelled = true;
|
|
54261
55617
|
};
|
|
54262
55618
|
}, [adapter, engine]);
|
|
55619
|
+
const configStore = config?.runtimeConfig;
|
|
55620
|
+
useEffect(() => {
|
|
55621
|
+
if (!configStore) return;
|
|
55622
|
+
let cancelled = false;
|
|
55623
|
+
withRuntimeConfigDefaults(configStore).load().then((rc) => {
|
|
55624
|
+
if (!cancelled) setLlmSelection({
|
|
55625
|
+
entries: rc.llm.entries,
|
|
55626
|
+
...rc.llm.defaultId ? { defaultId: rc.llm.defaultId } : {}
|
|
55627
|
+
});
|
|
55628
|
+
}).catch(() => void 0);
|
|
55629
|
+
return () => {
|
|
55630
|
+
cancelled = true;
|
|
55631
|
+
};
|
|
55632
|
+
}, [configStore, configVersion]);
|
|
55633
|
+
useEffect(() => {
|
|
55634
|
+
let cancelled = false;
|
|
55635
|
+
probeChromeBuiltinAvailability().then((result) => {
|
|
55636
|
+
if (!cancelled) setChromeBuiltin(result);
|
|
55637
|
+
});
|
|
55638
|
+
return () => {
|
|
55639
|
+
cancelled = true;
|
|
55640
|
+
};
|
|
55641
|
+
}, []);
|
|
55642
|
+
const composerModels = useMemo(() => {
|
|
55643
|
+
if (llmSelection.entries.length === 0) return void 0;
|
|
55644
|
+
return {
|
|
55645
|
+
options: llmSelection.entries.map((entry) => ({
|
|
55646
|
+
id: entry.id,
|
|
55647
|
+
label: entry.label,
|
|
55648
|
+
...entry.provider === "chrome-builtin" && chromeBuiltin?.available === false ? { disabled: true } : {}
|
|
55649
|
+
})),
|
|
55650
|
+
value: modelId ?? llmSelection.defaultId ?? llmSelection.entries[0]?.id ?? "",
|
|
55651
|
+
onChange: (id) => {
|
|
55652
|
+
setModelId(id);
|
|
55653
|
+
engine.setModel(id);
|
|
55654
|
+
}
|
|
55655
|
+
};
|
|
55656
|
+
}, [
|
|
55657
|
+
llmSelection,
|
|
55658
|
+
chromeBuiltin,
|
|
55659
|
+
modelId,
|
|
55660
|
+
engine
|
|
55661
|
+
]);
|
|
54263
55662
|
const refreshSessions = useCallback(async () => {
|
|
54264
55663
|
const page = await engine.listSessions();
|
|
54265
55664
|
setSessions(page.items);
|
|
@@ -54364,6 +55763,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54364
55763
|
setMessages(history.items);
|
|
54365
55764
|
setMessagesCursor(history.nextCursor);
|
|
54366
55765
|
setDrawerOpen(false);
|
|
55766
|
+
setModelId(engine.modelId);
|
|
54367
55767
|
}).catch((e) => setError({ message: toErrorMessage(e) }));
|
|
54368
55768
|
}, [engine]);
|
|
54369
55769
|
/** 向历史方向再取一页:页内升序,所以是**前插**而不是追加 */
|
|
@@ -54396,6 +55796,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54396
55796
|
setCurrentSessionId(meta.id);
|
|
54397
55797
|
setMessages([]);
|
|
54398
55798
|
setMessagesCursor(void 0);
|
|
55799
|
+
setModelId(engine.modelId);
|
|
54399
55800
|
await refreshSessions();
|
|
54400
55801
|
}).catch((e) => setError({ message: toErrorMessage(e) }));
|
|
54401
55802
|
}, [engine, refreshSessions]);
|
|
@@ -54425,6 +55826,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54425
55826
|
if (next.theme !== void 0) setTheme(next.theme);
|
|
54426
55827
|
if (next.locale !== void 0) setLocale(next.locale);
|
|
54427
55828
|
if (next.renderer !== void 0) setRenderer(next.renderer);
|
|
55829
|
+
if (next.dictationLang !== void 0) setDictationLang(next.dictationLang);
|
|
54428
55830
|
onAppearanceChange?.(next);
|
|
54429
55831
|
}, [onAppearanceChange]);
|
|
54430
55832
|
const chatRoot = (config?.chatRoot ?? "/chat").replace(/\/+$/, "");
|
|
@@ -54570,6 +55972,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54570
55972
|
disabled: interactionPending,
|
|
54571
55973
|
centered: composerCentered,
|
|
54572
55974
|
attachments: attachmentsReady,
|
|
55975
|
+
...dictationLang !== "" ? { dictationLang } : {},
|
|
55976
|
+
...composerModels ? { models: composerModels } : {},
|
|
54573
55977
|
...config?.placeholder ? { placeholder: config.placeholder } : {}
|
|
54574
55978
|
})]
|
|
54575
55979
|
})
|
|
@@ -54577,13 +55981,17 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
54577
55981
|
}),
|
|
54578
55982
|
/* @__PURE__ */ jsx(SettingsDrawer, {
|
|
54579
55983
|
open: settingsOpen,
|
|
54580
|
-
llmConfig: adapter.llmConfig,
|
|
54581
55984
|
...config?.runtimeConfig ? { runtimeConfig: config.runtimeConfig } : {},
|
|
54582
55985
|
theme,
|
|
54583
55986
|
locale,
|
|
54584
55987
|
renderer,
|
|
55988
|
+
dictationLang,
|
|
54585
55989
|
onAppearanceChange: handleAppearanceChange,
|
|
54586
|
-
|
|
55990
|
+
onClearFormAutofill: () => engine.clearFormAutofill(),
|
|
55991
|
+
onSaved: () => {
|
|
55992
|
+
engine.reloadConfig();
|
|
55993
|
+
setConfigVersion((version) => version + 1);
|
|
55994
|
+
},
|
|
54587
55995
|
onClose: () => setSettingsOpen(false)
|
|
54588
55996
|
})
|
|
54589
55997
|
]
|