@webskill/chatbot 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,11 +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 { BEHAVIOR_RECORDS_KEY, DEFAULT_LOOP_LIMITS, DEFAULT_USER_PROFILE_LIMITS, EventBus, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FsToolStepStore, FullDisclosureRouter, SUPPORTED_DOCUMENT_MIME, SerializingMemoryStore, SkillDiscovery, USER_PROFILE_KEY, WebSkillError, WebSkillRuntime, applyUserProfileImport, assertRemoteUrlAllowed, atomicWriteText, diffUserProfile, exportUserProfile, isUnsupportedRunSnapshot, messageOf, readBehaviorRecords, readUserProfile, refineUserProfile, summarizeRunUsage, summarizeToolCalls, textParts } from "@webskill/sdk";
9
+ import { BEHAVIOR_RECORDS_KEY, DEFAULT_LOOP_LIMITS, DEFAULT_USER_PROFILE_LIMITS, EventBus, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FsToolStepStore, FullDisclosureRouter, SUPPORTED_DOCUMENT_MIME, SerializingMemoryStore, SkillDiscovery, USER_PROFILE_KEY, WebSkillError, WebSkillRuntime, applyUserProfileImport, assertRemoteUrlAllowed, atomicWriteText, diffUserProfile, exportUserProfile, formatAttachmentText, isUnsupportedRunSnapshot, messageOf, readBehaviorRecords, readUserProfile, refineUserProfile, summarizeRunUsage, summarizeToolCalls, textParts } from "@webskill/sdk";
10
10
  import { BrowserWorkerScriptExecutor, checkDictationAvailability, compressImageToBudget, createEncryptedMemoryStore, createLlmClient, deleteMemoryEncryptionKey, openMemoryEncryptionKey, probeChromeBuiltinAvailability, startDictation } from "@webskill/sdk/browser";
11
11
  import { JsonRenderSpecSurface, NativeSpecSurface, OpenUiSpecSurface, ReactBridgeState, SpecInteractionChannel, SurfaceFormTextsProvider, UiSurfaceSnapshotList, probeJsonRenderAvailability } from "@webskill/sdk/ui-react";
12
12
  import { A2UI_SPEC_FORM_PATH, collectFormScopes, createUiCatalogToolSource, fromA2uiSpecAction, fromA2uiSurfaceAction, interactionToFormModel, interactionToUiSpec, loadOpenUiPeers, loadWebSkillLitCatalog, renderMiniChart, shapeInteractionValue, toA2uiSpecMessages, toA2uiSurfaceAction, toVercelToolInvocation, uiCatalog } from "@webskill/sdk/ui";
13
- import { TodoStore, createDelegationToolSource, createPageActionToolSource, createPagePerceptionToolSource, createSkillGenerationToolSource, createTodoToolSource, withDelegationOrigin } from "@webskill/sdk/agent";
13
+ import { DownloadedFilePolicy, PAGE_ACTION_KINDS, TodoStore, createDelegationToolSource, createDownloadedFileToolSource, createPageActionToolSource, createPagePerceptionToolSource, createSkillGenerationToolSource, createTodoToolSource, withDelegationOrigin } from "@webskill/sdk/agent";
14
14
  import * as React$1 from "react";
15
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";
16
16
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -30335,6 +30335,55 @@ const fadeInUp = {
30335
30335
  }
30336
30336
  };
30337
30337
 
30338
+ //#endregion
30339
+ //#region ../core/src/attachment/kind.ts
30340
+ /** 可作为图片分片外发的 MIME 类型 @experimental */
30341
+ const IMAGE_MIME_TYPES = [
30342
+ "image/png",
30343
+ "image/jpeg",
30344
+ "image/webp",
30345
+ "image/gif"
30346
+ ];
30347
+ /**
30348
+ * 可按文本读取的扩展名。accept 白名单与判定白名单共用这一份——
30349
+ * 0.5.x 两处不一致,`log`/`yaml`/`yml` 能通过判定却在文件选择器里选不到。
30350
+ */
30351
+ const TEXT_EXTENSIONS = [
30352
+ "txt",
30353
+ "md",
30354
+ "csv",
30355
+ "json",
30356
+ "log",
30357
+ "yaml",
30358
+ "yml"
30359
+ ];
30360
+ /** 非文本非图片但仍可整体外发的类型(provider 的 file 分片) @experimental */
30361
+ const FILE_MIME_TYPES = ["application/pdf"];
30362
+ /**
30363
+ * 需要**客户端先抽取成文本**才能外发的类型(FR-23.9 / 0.13.0 FR-12.5)。
30364
+ * 不走直通的 `file`:docx / xlsx 发给 provider 会被当成二进制垃圾。
30365
+ */
30366
+ const DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
30367
+ const XLSX_MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
30368
+ const TEXT_EXTENSION_SET = new Set(TEXT_EXTENSIONS);
30369
+ const IMAGE_MIME_SET = new Set(IMAGE_MIME_TYPES);
30370
+ const FILE_MIME_SET = new Set(FILE_MIME_TYPES);
30371
+ const EXTRACTED_MIME_SET = /* @__PURE__ */ new Set([DOCX_MIME, XLSX_MIME]);
30372
+ const EXTRACTED_EXTENSION_SET = /* @__PURE__ */ new Set(["docx", "xlsx"]);
30373
+ function extensionOf(fileName) {
30374
+ return fileName.split(".").pop()?.toLowerCase() ?? "";
30375
+ }
30376
+ /** 分类集中一处,不散落在各 UI 分支里 @experimental */
30377
+ function classifyAttachment(contentType, fileName) {
30378
+ const type = contentType.toLowerCase();
30379
+ if (IMAGE_MIME_SET.has(type)) return "image";
30380
+ if (EXTRACTED_MIME_SET.has(type)) return "document-text";
30381
+ if (FILE_MIME_SET.has(type)) return "file";
30382
+ if (type.startsWith("text/") || type === "application/json") return "text";
30383
+ if (type === "" && TEXT_EXTENSION_SET.has(extensionOf(fileName))) return "text";
30384
+ if (type === "" && EXTRACTED_EXTENSION_SET.has(extensionOf(fileName))) return "document-text";
30385
+ }
30386
+
30338
30387
  //#endregion
30339
30388
  //#region ../runtime/src/tools/types.ts
30340
30389
  /**
@@ -30534,11 +30583,13 @@ function defaultRuntimeConfig() {
30534
30583
  fetchData: false
30535
30584
  },
30536
30585
  maxDataSourceBytes: DEFAULT_MAX_DATA_SOURCE_BYTES,
30586
+ dataSources: [],
30537
30587
  remoteUrl: {
30538
30588
  allowHttp: false,
30539
30589
  allowPrivateHosts: false
30540
30590
  },
30541
- typescript: { enabled: false }
30591
+ typescript: { enabled: false },
30592
+ downloadedFiles: false
30542
30593
  },
30543
30594
  llm: { entries: [] },
30544
30595
  agentCapabilities: {
@@ -30644,7 +30695,8 @@ function mergeRuntimeConfigDefaults(partial) {
30644
30695
  typescript: {
30645
30696
  ...d.sandbox.typescript,
30646
30697
  ...p["sandbox"]?.["typescript"] ?? {}
30647
- }
30698
+ },
30699
+ dataSources: readDataSourceEntries(p["sandbox"]?.["dataSources"])
30648
30700
  },
30649
30701
  llm: mergeLlmSelection(p["llm"]),
30650
30702
  agentCapabilities: {
@@ -30711,6 +30763,29 @@ function readQuickPrompts(raw) {
30711
30763
  }
30712
30764
  return out;
30713
30765
  }
30766
+ /**
30767
+ * 逐条校验用户配的数据源:`id` / `url` 缺失或非字符串即整条丢弃。
30768
+ * 不整段回退,一条脏数据不该连坐其余几条。
30769
+ */
30770
+ function readDataSourceEntries(raw) {
30771
+ if (!Array.isArray(raw)) return [];
30772
+ const out = [];
30773
+ const seen = /* @__PURE__ */ new Set();
30774
+ for (const item of raw) {
30775
+ if (typeof item !== "object" || item === null) continue;
30776
+ const entry = item;
30777
+ const id = typeof entry["id"] === "string" ? entry["id"].trim() : "";
30778
+ const url = typeof entry["url"] === "string" ? entry["url"].trim() : "";
30779
+ if (id === "" || url === "" || seen.has(id)) continue;
30780
+ seen.add(id);
30781
+ out.push({
30782
+ id,
30783
+ url,
30784
+ description: typeof entry["description"] === "string" ? entry["description"] : ""
30785
+ });
30786
+ }
30787
+ return out;
30788
+ }
30714
30789
  /** 上限字段非正数时回退默认值:0 或负数会把整条通道变成永远拒收 */
30715
30790
  function mergeMultimodal(raw, d) {
30716
30791
  const p = typeof raw === "object" && raw !== null ? raw : {};
@@ -31513,7 +31588,6 @@ const chatbotDictionary = defineDictionary({
31513
31588
  "welcome.capability.transparency.title": "Run transparency",
31514
31589
  "welcome.capability.transparency.description": "Routing, skill activation, tool calls and results are visible live, step by step.",
31515
31590
  "welcome.quickPrompts": "Try an example",
31516
- "welcome.dynamicPrompts": "Suggested for this page",
31517
31591
  "message.copy": "Copy",
31518
31592
  "message.copied": "Copied",
31519
31593
  "message.retry": "Retry",
@@ -31599,11 +31673,20 @@ const chatbotDictionary = defineDictionary({
31599
31673
  "interaction.pageAction.click": "Allow the assistant to click “{target}”?",
31600
31674
  "interaction.pageAction.fill": "Allow the assistant to fill “{target}” with:",
31601
31675
  "interaction.pageAction.submit": "Allow the assistant to submit “{target}”?",
31676
+ "interaction.pageAction.select": "Allow the assistant to select an option in “{target}”?",
31677
+ "interaction.pageAction.set": "Allow the assistant to toggle “{target}”?",
31678
+ "interaction.pageAction.attach": "Allow the assistant to attach a file to “{target}”?",
31602
31679
  "interaction.pageAction.role.click": "this control",
31603
31680
  "interaction.pageAction.role.fill": "this field",
31604
31681
  "interaction.pageAction.role.submit": "this form",
31682
+ "interaction.pageAction.role.select": "this list",
31683
+ "interaction.pageAction.role.set": "this switch",
31684
+ "interaction.pageAction.role.attach": "this upload field",
31685
+ "interaction.pageAction.remember": "Don’t ask again for this kind of action",
31605
31686
  "interaction.pageAction.frame": "The target is in the embedded frame \"{frame}\".",
31606
31687
  "interaction.pageAction.elevated": "This dialog is outside the usual allowlist; it was opened by this task.",
31688
+ "interaction.downloadedFile.file": "{name} · {size}",
31689
+ "interaction.downloadedFile.remember": "Don’t ask again for this kind of access",
31607
31690
  "interaction.traceEvidence.title": "Steps this skill claims",
31608
31691
  "interaction.traceEvidence.columnDraft": "In the skill",
31609
31692
  "interaction.traceEvidence.columnTrace": "Actually run in this session",
@@ -31866,7 +31949,6 @@ const chatbotDictionary = defineDictionary({
31866
31949
  "welcome.capability.transparency.title": "运行透明",
31867
31950
  "welcome.capability.transparency.description": "路由、技能激活、工具调用与结果,逐步实时可见。",
31868
31951
  "welcome.quickPrompts": "试试这些示例",
31869
- "welcome.dynamicPrompts": "当前页面可用的快捷指令",
31870
31952
  "message.copy": "复制",
31871
31953
  "message.copied": "已复制",
31872
31954
  "message.edit": "编辑消息",
@@ -31952,11 +32034,20 @@ const chatbotDictionary = defineDictionary({
31952
32034
  "interaction.pageAction.click": "允许助手点击「{target}」吗?",
31953
32035
  "interaction.pageAction.fill": "允许助手在「{target}」中填入:",
31954
32036
  "interaction.pageAction.submit": "允许助手提交「{target}」吗?",
32037
+ "interaction.pageAction.select": "允许助手在「{target}」中选择选项吗?",
32038
+ "interaction.pageAction.set": "允许助手切换「{target}」吗?",
32039
+ "interaction.pageAction.attach": "允许助手向「{target}」附加文件吗?",
31955
32040
  "interaction.pageAction.role.click": "这个控件",
31956
32041
  "interaction.pageAction.role.fill": "这个输入框",
31957
32042
  "interaction.pageAction.role.submit": "这个表单",
32043
+ "interaction.pageAction.role.select": "这个选择框",
32044
+ "interaction.pageAction.role.set": "这个开关",
32045
+ "interaction.pageAction.role.attach": "这个上传控件",
32046
+ "interaction.pageAction.remember": "以后不再询问此类操作",
31958
32047
  "interaction.pageAction.frame": "目标位于嵌入帧「{frame}」中。",
31959
32048
  "interaction.pageAction.elevated": "该对话框不在常规允许清单内,是本次任务打开的。",
32049
+ "interaction.downloadedFile.file": "{name} · {size}",
32050
+ "interaction.downloadedFile.remember": "以后不再询问此类访问",
31960
32051
  "interaction.traceEvidence.title": "该技能声称的步骤",
31961
32052
  "interaction.traceEvidence.columnDraft": "技能里写的",
31962
32053
  "interaction.traceEvidence.columnTrace": "本会话实际执行的",
@@ -32329,8 +32420,6 @@ function redactInteractionValues(value, request) {
32329
32420
  }
32330
32421
  return out;
32331
32422
  }
32332
- /** 注入模型的单个附件正文上限(超出截断并标注) */
32333
- const ATTACHMENT_TEXT_LIMIT = 32 * 1024;
32334
32423
  /**
32335
32424
  * 行为记录与画像归属的 user 作用域标识。chatbot 是单人本地应用,没有账号体系,
32336
32425
  * 固定值即可;它只决定 memory 里的作用域名,不参与任何鉴权。
@@ -32780,6 +32869,17 @@ var ChatEngine = class {
32780
32869
  this.#maxHistoryMessages = void 0;
32781
32870
  }
32782
32871
  /**
32872
+ * 技能仓变更后调用(安装/卸载/发布):作废 runtime 缓存的技能目录,下次 send 重新扫描。
32873
+ *
32874
+ * 比 `reloadConfig()` 轻,因为它不清 `#handles`——那会连带丢掉每个会话的模型上下文。
32875
+ * 装技能的界面与对话不在同一个页面时(如扩展的 options 页 vs side panel),
32876
+ * 宿主必须自己把变更通知过来,否则新技能要等页面重载才出现在 catalog 里。
32877
+ * @experimental
32878
+ */
32879
+ invalidateSkills() {
32880
+ this.#ready?.then((runtime) => runtime.invalidate(), () => void 0);
32881
+ }
32882
+ /**
32783
32883
  * 会话列表页。归档会话也返回:UI 自己分区展示。
32784
32884
  * `limit` 不给默认值——缺省属于 `SessionStore` 实现,在这里兜底就等于只下推了一半。
32785
32885
  */
@@ -32978,11 +33078,14 @@ var ChatEngine = class {
32978
33078
  if (!await this.#adapter.storage.exists(path)) continue;
32979
33079
  if (attachment.kind === "text" || attachment.kind === "document-text") {
32980
33080
  const body = await this.#adapter.storage.readText(path);
32981
- const clipped = body.length > ATTACHMENT_TEXT_LIMIT ? `${body.slice(0, ATTACHMENT_TEXT_LIMIT)}\n[truncated]` : body;
32982
- const label = attachment.kind === "document-text" ? `${attachment.name} (${attachment.contentType}, body text only)` : `${attachment.name} (${attachment.contentType})`;
32983
33081
  parts.push({
32984
33082
  type: "text",
32985
- text: `--- Attachment: ${label} ---\n${clipped}`
33083
+ text: formatAttachmentText({
33084
+ name: attachment.name,
33085
+ contentType: attachment.contentType,
33086
+ body,
33087
+ kind: attachment.kind
33088
+ })
32986
33089
  });
32987
33090
  continue;
32988
33091
  }
@@ -33332,6 +33435,7 @@ var ChatEngine = class {
33332
33435
  })] : [],
33333
33436
  ...pagePerception ? [createPagePerceptionToolSource({
33334
33437
  policy: pagePerception,
33438
+ ...this.#adapter.pagePerceptionPaging === void 0 ? {} : { paging: () => this.#adapter.pagePerceptionPaging },
33335
33439
  imageCapture: async () => {
33336
33440
  const live = await this.#runtimeConfigStore()?.load();
33337
33441
  return {
@@ -33342,7 +33446,19 @@ var ChatEngine = class {
33342
33446
  };
33343
33447
  }
33344
33448
  })] : [],
33345
- ...this.#adapter.pageActions ? [createPageActionToolSource({ policy: this.#adapter.pageActions })] : []
33449
+ ...this.#adapter.pageActions ? [createPageActionToolSource({ policy: this.#adapter.pageActions })] : [],
33450
+ ...this.#adapter.downloads ? [createDownloadedFileToolSource({ policy: new DownloadedFilePolicy({
33451
+ reader: this.#adapter.downloads.reader,
33452
+ ui: this.#uiBridge,
33453
+ capabilityEnabled: () => rc?.sandbox.downloadedFiles === true,
33454
+ imageCapable: async () => {
33455
+ return entryCapabilities(pickLlmEntry(await this.#runtimeConfigStore()?.load(), this.#selectedModelId)).image;
33456
+ },
33457
+ ...this.#adapter.downloads.consent ? { consent: this.#adapter.downloads.consent } : {},
33458
+ ...this.#adapter.docxExtractor ? { docxExtractor: this.#adapter.docxExtractor } : {},
33459
+ ...this.#adapter.xlsxExtractor ? { xlsxExtractor: this.#adapter.xlsxExtractor } : {},
33460
+ ...this.#adapter.documentAudit ? { audit: this.#adapter.documentAudit } : {}
33461
+ }) })] : []
33346
33462
  ];
33347
33463
  const deps = {
33348
33464
  fs,
@@ -33377,6 +33493,7 @@ var ChatEngine = class {
33377
33493
  ...Object.keys(loopConfig).length > 0 ? { config: loopConfig } : {},
33378
33494
  ...model ? { model } : {},
33379
33495
  ...this.#options.fetchData ? { fetchData: this.#options.fetchData } : {},
33496
+ ...this.#options.dataSources ? { dataSources: this.#options.dataSources } : {},
33380
33497
  ...this.#adapter.linkedDocuments ? { linkedDocuments: this.#adapter.linkedDocuments } : {},
33381
33498
  ...this.#adapter.docxExtractor ? { docxExtractor: this.#adapter.docxExtractor } : {},
33382
33499
  ...this.#adapter.xlsxExtractor ? { xlsxExtractor: this.#adapter.xlsxExtractor } : {},
@@ -34656,33 +34773,10 @@ function AssistantUiEmptyState({ title, prompts, dynamicPrompts, limit, disabled
34656
34773
  });
34657
34774
  }
34658
34775
  /**
34659
- * 对话进行中的动态快捷指令条(FR-17.6)。
34660
- * 只接**动态**那一批:静态清单不随上下文变,常驻只会变成噪声。
34661
- * 空列表时整条不渲染,DOM 里不留空容器。
34776
+ * 对话进行中的动态快捷指令条(0.13.0 FR-17.6)已废止:
34777
+ * 每轮对话结束都在输入区上方冒出一条横向滚动的胶囊带,构成持续干扰。
34778
+ * `ChatbotConfig.dynamicQuickPrompts` 保留,但只在空态(`AssistantUiEmptyState`)生效。
34662
34779
  */
34663
- function AssistantUiDynamicPrompts({ prompts, limit, onPrompt }) {
34664
- const t = useT();
34665
- const items = mergeQuickPrompts(prompts, [], useLocale(), limit);
34666
- if (items.length === 0) return null;
34667
- return /* @__PURE__ */ jsx("div", {
34668
- "data-testid": "chatbot-dynamic-prompts",
34669
- "aria-label": t("welcome.dynamicPrompts"),
34670
- className: "flex gap-2 overflow-x-auto px-4 pb-2",
34671
- children: items.map((item, index) => {
34672
- const Icon = resolveQuickPromptIcon(item.icon);
34673
- return /* @__PURE__ */ jsxs("button", {
34674
- type: "button",
34675
- "data-testid": "chatbot-dynamic-prompt",
34676
- onClick: () => onPrompt(item.text),
34677
- className: "inline-flex shrink-0 items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1.5 text-xs text-muted transition-colors hover:bg-subtle hover:text-ink",
34678
- children: [Icon ? /* @__PURE__ */ jsx(Icon, {
34679
- className: "size-3.5 shrink-0",
34680
- "aria-hidden": true
34681
- }) : null, item.text]
34682
- }, `${index}:${item.text}`);
34683
- })
34684
- });
34685
- }
34686
34780
 
34687
34781
  //#endregion
34688
34782
  //#region src/react/format.ts
@@ -46697,52 +46791,6 @@ function AssistantUiMessage(props) {
46697
46791
 
46698
46792
  //#endregion
46699
46793
  //#region src/core/attachmentKind.ts
46700
- /** 可作为图片分片外发的 MIME 类型 @experimental */
46701
- const IMAGE_MIME_TYPES = [
46702
- "image/png",
46703
- "image/jpeg",
46704
- "image/webp",
46705
- "image/gif"
46706
- ];
46707
- /**
46708
- * 可按文本读取的扩展名。accept 白名单与判定白名单共用这一份——
46709
- * 0.5.x 两处不一致,`log`/`yaml`/`yml` 能通过判定却在文件选择器里选不到。
46710
- */
46711
- const TEXT_EXTENSIONS = [
46712
- "txt",
46713
- "md",
46714
- "csv",
46715
- "json",
46716
- "log",
46717
- "yaml",
46718
- "yml"
46719
- ];
46720
- const TEXT_EXTENSION_SET = new Set(TEXT_EXTENSIONS);
46721
- const IMAGE_MIME_SET = new Set(IMAGE_MIME_TYPES);
46722
- /** 非文本非图片但仍可整体外发的类型(provider 的 file 分片) */
46723
- const FILE_MIME_TYPES = ["application/pdf"];
46724
- const FILE_MIME_SET = new Set(FILE_MIME_TYPES);
46725
- /**
46726
- * 需要**客户端先抽取成文本**才能外发的类型(FR-23.9 / 0.13.0 FR-12.5)。
46727
- * 不走直通的 `file`:docx / xlsx 发给 provider 会被当成二进制垃圾。
46728
- */
46729
- const DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
46730
- const XLSX_MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
46731
- const EXTRACTED_MIME_SET = /* @__PURE__ */ new Set([DOCX_MIME, XLSX_MIME]);
46732
- const EXTRACTED_EXTENSION_SET = /* @__PURE__ */ new Set(["docx", "xlsx"]);
46733
- function extensionOf(fileName) {
46734
- return fileName.split(".").pop()?.toLowerCase() ?? "";
46735
- }
46736
- /** 分类集中一处,不散落在各 UI 分支里 @experimental */
46737
- function classifyAttachment(contentType, fileName) {
46738
- const type = contentType.toLowerCase();
46739
- if (IMAGE_MIME_SET.has(type)) return "image";
46740
- if (EXTRACTED_MIME_SET.has(type)) return "document-text";
46741
- if (FILE_MIME_SET.has(type)) return "file";
46742
- if (type.startsWith("text/") || type === "application/json") return "text";
46743
- if (type === "" && TEXT_EXTENSION_SET.has(extensionOf(fileName))) return "text";
46744
- if (type === "" && EXTRACTED_EXTENSION_SET.has(extensionOf(fileName))) return "document-text";
46745
- }
46746
46794
  const TEXT_ACCEPT = [
46747
46795
  "text/plain",
46748
46796
  "text/markdown",
@@ -47264,18 +47312,43 @@ function pageActionDetails(details) {
47264
47312
  const record = details;
47265
47313
  const action = record["action"];
47266
47314
  const target = record["target"];
47267
- if (action !== "click" && action !== "fill" && action !== "submit") return void 0;
47315
+ if (typeof action !== "string" || !PAGE_ACTION_KINDS.includes(action)) return void 0;
47268
47316
  if (typeof target !== "object" || target === null) return void 0;
47269
47317
  const { role, name, frame } = target;
47270
47318
  if (typeof role !== "string") return void 0;
47271
47319
  const value = record["value"];
47320
+ const rememberLabel = record["rememberLabel"];
47272
47321
  return {
47273
47322
  action,
47274
47323
  role,
47275
47324
  ...typeof name === "string" ? { name } : {},
47276
47325
  ...typeof value === "string" ? { value } : {},
47277
47326
  ...typeof frame === "string" ? { frame } : {},
47278
- ...record["elevated"] === true ? { elevated: true } : {}
47327
+ ...record["elevated"] === true ? { elevated: true } : {},
47328
+ ...record["rememberable"] === true ? { rememberable: true } : {},
47329
+ ...typeof rememberLabel === "string" ? { rememberLabel } : {}
47330
+ };
47331
+ }
47332
+ /**
47333
+ * 下载文件确认卡的载荷(0.14.0 分册 20)。与 `pageActionDetails` 同一条规矩:
47334
+ * 形状不对就整段不渲染,退回通用授权展示,不猜。
47335
+ */
47336
+ function downloadedFileDetails(details) {
47337
+ if (typeof details !== "object" || details === null) return void 0;
47338
+ const record = details;
47339
+ const action = record["action"];
47340
+ if (action !== "list" && action !== "read") return void 0;
47341
+ const file = record["file"];
47342
+ const rememberLabel = record["rememberLabel"];
47343
+ const named = typeof file === "object" && file !== null ? file : void 0;
47344
+ return {
47345
+ action,
47346
+ ...typeof named?.["name"] === "string" && typeof named["size"] === "number" ? { file: {
47347
+ name: named["name"],
47348
+ size: named["size"]
47349
+ } } : {},
47350
+ ...record["rememberable"] === true ? { rememberable: true } : {},
47351
+ ...typeof rememberLabel === "string" ? { rememberLabel } : {}
47279
47352
  };
47280
47353
  }
47281
47354
  /**
@@ -47308,8 +47381,10 @@ function traceEvidenceDetails(details) {
47308
47381
  }
47309
47382
  function ActiveInteractionCard({ request, bridge }) {
47310
47383
  const t = useT();
47384
+ const [remember, setRemember] = useState(false);
47311
47385
  const isAuthorize = request.type === "authorize";
47312
47386
  const pageAction = isAuthorize && request.capability === "pageAction" ? pageActionDetails(request.details) : void 0;
47387
+ const downloadedFile = isAuthorize && request.capability === "readDownloadedFile" ? downloadedFileDetails(request.details) : void 0;
47313
47388
  const traceEvidence = isAuthorize ? traceEvidenceDetails(request.details) : void 0;
47314
47389
  const spec = interactionToUiSpec(request.type === "authorize" && pageAction !== void 0 ? {
47315
47390
  ...request,
@@ -47337,7 +47412,8 @@ function ActiveInteractionCard({ request, bridge }) {
47337
47412
  const value = shapeInteractionValue(interactionToFormModel(request), event.value ?? {});
47338
47413
  bridge.resolve({
47339
47414
  id: request.id,
47340
- value
47415
+ value,
47416
+ ...remember ? { remembered: true } : {}
47341
47417
  });
47342
47418
  };
47343
47419
  return /* @__PURE__ */ jsxs(motion.div, {
@@ -47413,8 +47489,33 @@ function ActiveInteractionCard({ request, bridge }) {
47413
47489
  "data-testid": "interaction-page-action-elevated",
47414
47490
  className: "text-sm font-medium text-warning",
47415
47491
  children: t("interaction.pageAction.elevated")
47492
+ }) : null,
47493
+ pageAction.rememberable === true ? /* @__PURE__ */ jsxs("label", {
47494
+ className: "flex items-center gap-2 text-sm text-ink",
47495
+ children: [/* @__PURE__ */ jsx("input", {
47496
+ type: "checkbox",
47497
+ "data-testid": "interaction-page-action-remember",
47498
+ checked: remember,
47499
+ onChange: (event) => setRemember(event.currentTarget.checked)
47500
+ }), pageAction.rememberLabel ?? t("interaction.pageAction.remember")]
47416
47501
  }) : null
47417
47502
  ] }) : null,
47503
+ downloadedFile ? /* @__PURE__ */ jsxs(Fragment$1, { children: [downloadedFile.file ? /* @__PURE__ */ jsx("p", {
47504
+ "data-testid": "interaction-downloaded-file",
47505
+ className: "text-sm text-ink",
47506
+ children: t("interaction.downloadedFile.file", {
47507
+ name: downloadedFile.file.name,
47508
+ size: formatBytes(downloadedFile.file.size)
47509
+ })
47510
+ }) : null, downloadedFile.rememberable === true ? /* @__PURE__ */ jsxs("label", {
47511
+ className: "flex items-center gap-2 text-sm text-ink",
47512
+ children: [/* @__PURE__ */ jsx("input", {
47513
+ type: "checkbox",
47514
+ "data-testid": "interaction-downloaded-file-remember",
47515
+ checked: remember,
47516
+ onChange: (event) => setRemember(event.currentTarget.checked)
47517
+ }), downloadedFile.rememberLabel ?? t("interaction.downloadedFile.remember")]
47518
+ }) : null] }) : null,
47418
47519
  traceEvidence ? /* @__PURE__ */ jsxs("div", {
47419
47520
  "data-testid": "interaction-trace-evidence",
47420
47521
  children: [/* @__PURE__ */ jsx("div", {
@@ -47894,6 +47995,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
47894
47995
  ...config?.governance ? { governance: config.governance } : {},
47895
47996
  ...config?.skillCandidates ? { skillCandidates: config.skillCandidates } : {},
47896
47997
  ...config?.fetchData ? { fetchData: config.fetchData } : {},
47998
+ ...config?.dataSources ? { dataSources: config.dataSources } : {},
47999
+ ...config?.executorFactory ? { executorFactory: config.executorFactory } : {},
47897
48000
  ...rendererProp ? { renderer: rendererProp } : {}
47898
48001
  }), [
47899
48002
  adapter,
@@ -47906,6 +48009,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
47906
48009
  config?.governance,
47907
48010
  config?.skillCandidates,
47908
48011
  config?.fetchData,
48012
+ config?.dataSources,
48013
+ config?.executorFactory,
47909
48014
  rendererProp
47910
48015
  ]);
47911
48016
  useEffect(() => {
@@ -48089,7 +48194,9 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
48089
48194
  unsubscribe?.();
48090
48195
  };
48091
48196
  }, [configStore, engine]);
48197
+ const hasBuiltinEntry = llmSelection.entries.some((entry) => entry.provider === "chrome-builtin");
48092
48198
  useEffect(() => {
48199
+ if (!hasBuiltinEntry) return void 0;
48093
48200
  let cancelled = false;
48094
48201
  probeChromeBuiltinAvailability().then((result) => {
48095
48202
  if (!cancelled) setChromeBuiltin(result);
@@ -48097,7 +48204,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
48097
48204
  return () => {
48098
48205
  cancelled = true;
48099
48206
  };
48100
- }, []);
48207
+ }, [hasBuiltinEntry]);
48101
48208
  /** 选中模型条目 id:能力受限提示、设置面板置灰与附件门槛都以它为准 */
48102
48209
  const selectedModelId = modelId ?? llmSelection.defaultId ?? llmSelection.entries[0]?.id ?? "";
48103
48210
  const selectedEntry = llmSelection.entries.find((entry) => entry.id === selectedModelId);
@@ -48557,11 +48664,6 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
48557
48664
  } : {},
48558
48665
  onLocate: handleLocateInteraction
48559
48666
  }),
48560
- messages.length > 0 && !sending && config?.dynamicQuickPrompts ? /* @__PURE__ */ jsx(AssistantUiDynamicPrompts, {
48561
- prompts: config.dynamicQuickPrompts,
48562
- limit: quickPromptLimit,
48563
- onPrompt: handleSend
48564
- }) : null,
48565
48667
  /* @__PURE__ */ jsx(AssistantUiComposer, {
48566
48668
  ...modelCapabilities ? { capabilities: {
48567
48669
  tools: modelCapabilities.tools,
@@ -48654,7 +48756,7 @@ function VercelPayloadPreview({ bridge }) {
48654
48756
  * Version of the published `@webskill/chatbot` package, injected at build time.
48655
48757
  * @stable
48656
48758
  */
48657
- const CHATBOT_VERSION = "0.10.0";
48759
+ const CHATBOT_VERSION = "0.11.0";
48658
48760
 
48659
48761
  //#endregion
48660
48762
  export { A2uiSurfaceHost, A2uiSurfaceSnapshotHost, CHATBOT_VERSION, ChatEngine, Chatbot, CompositeUiBridge, DEFAULT_RENDERER_CAPABILITIES, DEFAULT_RUNTIME_CONFIG_STORAGE_KEY, ErrorCard, InteractionCard, InterruptedBanner, OpenUiSurfaceHost, OpenUiSurfaceSnapshotHost, ResultBlockList, ResultBlocksPro, SkillBadges, SpecInteraction, VercelPayloadPreview, VercelSurfaceHost, VercelSurfaceSnapshotHost, chatbotDictionary, configureA2uiMarkdown, createLocalStorageRuntimeConfigStore, createMemoryRuntimeConfigStore, isLlmEntryUsable, pickUsableLlmEntry, probeA2uiAvailability, probeOpenUiAvailability, sandboxExecutorDeps, useT };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webskill/chatbot",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -26,7 +26,7 @@
26
26
  "@a2ui/web_core": "^0.10.5",
27
27
  "@lit/context": "^1.1.0",
28
28
  "@openuidev/react-lang": "0.2.8",
29
- "@webskill/sdk": "^0.13.0",
29
+ "@webskill/sdk": "^0.14.0",
30
30
  "react": ">=19.0.0",
31
31
  "react-dom": ">=19.0.0",
32
32
  "zod": "^3.25.0 || ^4.0.0"