@webskill/chatbot 0.10.0 → 0.12.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, assertSafePathSegment, atomicWriteText, diffUserProfile, exportUserProfile, formatAttachmentText, isUnsupportedRunSnapshot, messageOf, readBehaviorRecords, readUserProfile, refineUserProfile, resolveInsideRoot, sanitizeUntrustedLine, 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
  /**
@@ -30367,6 +30416,11 @@ const DEFAULT_MAX_DOCUMENT_TEXT_BYTES = 512e3;
30367
30416
  * 结构化数据不是文档,量级差一个数量级;超出**只拒不截**——截断的 JSON 解不出来。
30368
30417
  */
30369
30418
  const DEFAULT_MAX_DATA_SOURCE_BYTES = 1024e3;
30419
+ /**
30420
+ * 单个上传文件交给脚本的字节上限(0.15.0 分册 17,FR-17.5):20 MB。
30421
+ * 取得比模型图片预算宽:字节不进模型上下文,只进脚本,护的是内存不是 token。
30422
+ */
30423
+ const DEFAULT_MAX_UPLOAD_FILE_BYTES = 20 * 1024 * 1024;
30370
30424
 
30371
30425
  //#endregion
30372
30426
  //#region ../runtime/src/engine/limits.ts
@@ -30534,11 +30588,15 @@ function defaultRuntimeConfig() {
30534
30588
  fetchData: false
30535
30589
  },
30536
30590
  maxDataSourceBytes: DEFAULT_MAX_DATA_SOURCE_BYTES,
30591
+ dataSources: [],
30537
30592
  remoteUrl: {
30538
30593
  allowHttp: false,
30539
30594
  allowPrivateHosts: false
30540
30595
  },
30541
- typescript: { enabled: false }
30596
+ typescript: { enabled: false },
30597
+ downloadedFiles: false,
30598
+ uploadFiles: false,
30599
+ maxUploadFileBytes: DEFAULT_MAX_UPLOAD_FILE_BYTES
30542
30600
  },
30543
30601
  llm: { entries: [] },
30544
30602
  agentCapabilities: {
@@ -30644,7 +30702,8 @@ function mergeRuntimeConfigDefaults(partial) {
30644
30702
  typescript: {
30645
30703
  ...d.sandbox.typescript,
30646
30704
  ...p["sandbox"]?.["typescript"] ?? {}
30647
- }
30705
+ },
30706
+ dataSources: readDataSourceEntries(p["sandbox"]?.["dataSources"])
30648
30707
  },
30649
30708
  llm: mergeLlmSelection(p["llm"]),
30650
30709
  agentCapabilities: {
@@ -30711,6 +30770,29 @@ function readQuickPrompts(raw) {
30711
30770
  }
30712
30771
  return out;
30713
30772
  }
30773
+ /**
30774
+ * 逐条校验用户配的数据源:`id` / `url` 缺失或非字符串即整条丢弃。
30775
+ * 不整段回退,一条脏数据不该连坐其余几条。
30776
+ */
30777
+ function readDataSourceEntries(raw) {
30778
+ if (!Array.isArray(raw)) return [];
30779
+ const out = [];
30780
+ const seen = /* @__PURE__ */ new Set();
30781
+ for (const item of raw) {
30782
+ if (typeof item !== "object" || item === null) continue;
30783
+ const entry = item;
30784
+ const id = typeof entry["id"] === "string" ? entry["id"].trim() : "";
30785
+ const url = typeof entry["url"] === "string" ? entry["url"].trim() : "";
30786
+ if (id === "" || url === "" || seen.has(id)) continue;
30787
+ seen.add(id);
30788
+ out.push({
30789
+ id,
30790
+ url,
30791
+ description: typeof entry["description"] === "string" ? entry["description"] : ""
30792
+ });
30793
+ }
30794
+ return out;
30795
+ }
30714
30796
  /** 上限字段非正数时回退默认值:0 或负数会把整条通道变成永远拒收 */
30715
30797
  function mergeMultimodal(raw, d) {
30716
30798
  const p = typeof raw === "object" && raw !== null ? raw : {};
@@ -31513,7 +31595,6 @@ const chatbotDictionary = defineDictionary({
31513
31595
  "welcome.capability.transparency.title": "Run transparency",
31514
31596
  "welcome.capability.transparency.description": "Routing, skill activation, tool calls and results are visible live, step by step.",
31515
31597
  "welcome.quickPrompts": "Try an example",
31516
- "welcome.dynamicPrompts": "Suggested for this page",
31517
31598
  "message.copy": "Copy",
31518
31599
  "message.copied": "Copied",
31519
31600
  "message.retry": "Retry",
@@ -31599,11 +31680,20 @@ const chatbotDictionary = defineDictionary({
31599
31680
  "interaction.pageAction.click": "Allow the assistant to click “{target}”?",
31600
31681
  "interaction.pageAction.fill": "Allow the assistant to fill “{target}” with:",
31601
31682
  "interaction.pageAction.submit": "Allow the assistant to submit “{target}”?",
31683
+ "interaction.pageAction.select": "Allow the assistant to select an option in “{target}”?",
31684
+ "interaction.pageAction.set": "Allow the assistant to toggle “{target}”?",
31685
+ "interaction.pageAction.attach": "Allow the assistant to attach a file to “{target}”?",
31602
31686
  "interaction.pageAction.role.click": "this control",
31603
31687
  "interaction.pageAction.role.fill": "this field",
31604
31688
  "interaction.pageAction.role.submit": "this form",
31689
+ "interaction.pageAction.role.select": "this list",
31690
+ "interaction.pageAction.role.set": "this switch",
31691
+ "interaction.pageAction.role.attach": "this upload field",
31692
+ "interaction.pageAction.remember": "Don’t ask again for this kind of action",
31605
31693
  "interaction.pageAction.frame": "The target is in the embedded frame \"{frame}\".",
31606
31694
  "interaction.pageAction.elevated": "This dialog is outside the usual allowlist; it was opened by this task.",
31695
+ "interaction.downloadedFile.file": "{name} · {size}",
31696
+ "interaction.downloadedFile.remember": "Don’t ask again for this kind of access",
31607
31697
  "interaction.traceEvidence.title": "Steps this skill claims",
31608
31698
  "interaction.traceEvidence.columnDraft": "In the skill",
31609
31699
  "interaction.traceEvidence.columnTrace": "Actually run in this session",
@@ -31625,6 +31715,7 @@ const chatbotDictionary = defineDictionary({
31625
31715
  "surface.readOnlySnapshot": "This is a saved record — its actions are no longer available.",
31626
31716
  "surface.suggested": "Suggested:",
31627
31717
  "surface.useSuggestion": "Use",
31718
+ "surface.openDocument": "Open document",
31628
31719
  "surface.control.previousPage": "Previous page",
31629
31720
  "surface.control.previous": "Previous",
31630
31721
  "surface.control.nextPage": "Next page",
@@ -31866,7 +31957,6 @@ const chatbotDictionary = defineDictionary({
31866
31957
  "welcome.capability.transparency.title": "运行透明",
31867
31958
  "welcome.capability.transparency.description": "路由、技能激活、工具调用与结果,逐步实时可见。",
31868
31959
  "welcome.quickPrompts": "试试这些示例",
31869
- "welcome.dynamicPrompts": "当前页面可用的快捷指令",
31870
31960
  "message.copy": "复制",
31871
31961
  "message.copied": "已复制",
31872
31962
  "message.edit": "编辑消息",
@@ -31952,11 +32042,20 @@ const chatbotDictionary = defineDictionary({
31952
32042
  "interaction.pageAction.click": "允许助手点击「{target}」吗?",
31953
32043
  "interaction.pageAction.fill": "允许助手在「{target}」中填入:",
31954
32044
  "interaction.pageAction.submit": "允许助手提交「{target}」吗?",
32045
+ "interaction.pageAction.select": "允许助手在「{target}」中选择选项吗?",
32046
+ "interaction.pageAction.set": "允许助手切换「{target}」吗?",
32047
+ "interaction.pageAction.attach": "允许助手向「{target}」附加文件吗?",
31955
32048
  "interaction.pageAction.role.click": "这个控件",
31956
32049
  "interaction.pageAction.role.fill": "这个输入框",
31957
32050
  "interaction.pageAction.role.submit": "这个表单",
32051
+ "interaction.pageAction.role.select": "这个选择框",
32052
+ "interaction.pageAction.role.set": "这个开关",
32053
+ "interaction.pageAction.role.attach": "这个上传控件",
32054
+ "interaction.pageAction.remember": "以后不再询问此类操作",
31958
32055
  "interaction.pageAction.frame": "目标位于嵌入帧「{frame}」中。",
31959
32056
  "interaction.pageAction.elevated": "该对话框不在常规允许清单内,是本次任务打开的。",
32057
+ "interaction.downloadedFile.file": "{name} · {size}",
32058
+ "interaction.downloadedFile.remember": "以后不再询问此类访问",
31960
32059
  "interaction.traceEvidence.title": "该技能声称的步骤",
31961
32060
  "interaction.traceEvidence.columnDraft": "技能里写的",
31962
32061
  "interaction.traceEvidence.columnTrace": "本会话实际执行的",
@@ -31978,6 +32077,7 @@ const chatbotDictionary = defineDictionary({
31978
32077
  "surface.readOnlySnapshot": "这是已保存的记录,其中的操作已不可用。",
31979
32078
  "surface.suggested": "建议:",
31980
32079
  "surface.useSuggestion": "使用",
32080
+ "surface.openDocument": "打开文档",
31981
32081
  "surface.control.previousPage": "上一页",
31982
32082
  "surface.control.previous": "上一页",
31983
32083
  "surface.control.nextPage": "下一页",
@@ -32329,8 +32429,6 @@ function redactInteractionValues(value, request) {
32329
32429
  }
32330
32430
  return out;
32331
32431
  }
32332
- /** 注入模型的单个附件正文上限(超出截断并标注) */
32333
- const ATTACHMENT_TEXT_LIMIT = 32 * 1024;
32334
32432
  /**
32335
32433
  * 行为记录与画像归属的 user 作用域标识。chatbot 是单人本地应用,没有账号体系,
32336
32434
  * 固定值即可;它只决定 memory 里的作用域名,不参与任何鉴权。
@@ -32508,11 +32606,18 @@ var ChatEngine = class {
32508
32606
  #ready;
32509
32607
  /** 已装配的 runtime(cancel 等同步委托用;未装配时为 undefined) */
32510
32608
  #runtime;
32609
+ /**
32610
+ * 每轮 `send()` 开头刷新的运行时配置快照(0.15.0 分册 15 · FR-15.6)。
32611
+ * 只给那些必须同步回答、又不能等到重建运行时的开关用。
32612
+ */
32613
+ #liveConfig;
32511
32614
  #handles = /* @__PURE__ */ new Map();
32512
32615
  #currentSessionId;
32513
32616
  #listeners = /* @__PURE__ */ new Set();
32514
32617
  /** runId → 已激活技能累积(skill-activated 事件用;终态清理) */
32515
32618
  #runSkills = /* @__PURE__ */ new Map();
32619
+ /** 文档投放端口只构造一次:渲染侧把它放进 memo 依赖,每次取新对象会触发无谓重渲染 */
32620
+ #documentSurfacePort;
32516
32621
  /** 当前渲染框架档(复合 uiBridge 分发;setRenderer 热切换) */
32517
32622
  #renderer;
32518
32623
  /** 非 native 三档共用的交互通道(Chatbot 的 SpecInteraction attach 后生效) */
@@ -32721,6 +32826,52 @@ var ChatEngine = class {
32721
32826
  get interactionChannel() {
32722
32827
  return this.#interactionChannel;
32723
32828
  }
32829
+ /**
32830
+ * 文档投放端口(0.15.0 分册 13)。宿主没注入 `adapter.documentSurface` 时为 `undefined`,
32831
+ * 渲染侧据此整个不渲染按钮。
32832
+ *
32833
+ * 端口实现落在引擎里而不是宿主里,是因为「产物在哪、技能叫什么」这两件事只有引擎知道:
32834
+ * 交给宿主就等于把产物目录布局变成宿主必须复刻的知识,两个宿主各抄一份必然分叉,
32835
+ * 而那正是本册要消灭的东西。
32836
+ * @experimental
32837
+ */
32838
+ get documentSurface() {
32839
+ const host = this.#adapter.documentSurface;
32840
+ if (!host) return void 0;
32841
+ return this.#documentSurfacePort ??= { open: async ({ runId, artifact, style, dataSource }) => {
32842
+ const dir = `${this.#chatRoot}/artifacts/${runId}`;
32843
+ const html = await this.#adapter.storage.readText(this.#artifactPath(dir, artifact));
32844
+ const css = style === void 0 ? void 0 : await this.#adapter.storage.readText(this.#artifactPath(dir, style));
32845
+ await host.open({
32846
+ skillName: await this.#skillNamesOfRun(runId),
32847
+ dataSource: sanitizeUntrustedLine(dataSource),
32848
+ document: {
32849
+ html,
32850
+ ...css === void 0 ? {} : { css }
32851
+ }
32852
+ });
32853
+ } };
32854
+ }
32855
+ /**
32856
+ * 产物名 → 绝对路径。`assertSafePathSegment` + `resolveInsideRoot` 两道都不是可选写法:
32857
+ * 少了它们,「组件只收产物名」就退化成「组件收完整路径」,凭空开出一个读取面。
32858
+ */
32859
+ #artifactPath(dir, artifact) {
32860
+ assertSafePathSegment(artifact, "artifact");
32861
+ return resolveInsideRoot(dir, artifact);
32862
+ }
32863
+ /**
32864
+ * 这次 run 激活过的技能名。它是授权告知的主语,因此**不能**由发出 surface 的技能自报——
32865
+ * 那等于让被授权方自己写授权提示。
32866
+ *
32867
+ * 进行中的 run 读内存累积;已结束的(历史消息里的按钮就是这种)读 trace,
32868
+ * 只读内存会在 run 收尾清表后得到空串。
32869
+ */
32870
+ async #skillNamesOfRun(runId) {
32871
+ const live = this.#runSkills.get(runId);
32872
+ if (live && live.size > 0) return [...live].sort().join(", ");
32873
+ return [...(await this.#traceStore.get(runId).catch(() => void 0))?.activeSkills ?? []].sort().join(", ");
32874
+ }
32724
32875
  /** 探测 @a2ui/lit 是否安装(Appearance/设置区 A2UI 档置灰判定) */
32725
32876
  probeA2uiAvailability() {
32726
32877
  return probeA2uiAvailability();
@@ -32780,6 +32931,17 @@ var ChatEngine = class {
32780
32931
  this.#maxHistoryMessages = void 0;
32781
32932
  }
32782
32933
  /**
32934
+ * 技能仓变更后调用(安装/卸载/发布):作废 runtime 缓存的技能目录,下次 send 重新扫描。
32935
+ *
32936
+ * 比 `reloadConfig()` 轻,因为它不清 `#handles`——那会连带丢掉每个会话的模型上下文。
32937
+ * 装技能的界面与对话不在同一个页面时(如扩展的 options 页 vs side panel),
32938
+ * 宿主必须自己把变更通知过来,否则新技能要等页面重载才出现在 catalog 里。
32939
+ * @experimental
32940
+ */
32941
+ invalidateSkills() {
32942
+ this.#ready?.then((runtime) => runtime.invalidate(), () => void 0);
32943
+ }
32944
+ /**
32783
32945
  * 会话列表页。归档会话也返回:UI 自己分区展示。
32784
32946
  * `limit` 不给默认值——缺省属于 `SessionStore` 实现,在这里兜底就等于只下推了一半。
32785
32947
  */
@@ -32907,6 +33069,7 @@ var ChatEngine = class {
32907
33069
  async send(text, attachments = []) {
32908
33070
  if (this.#disposed) throw new WebSkillError("RUN_FAILED", "This ChatEngine was disposed; create a new one to keep chatting.");
32909
33071
  if (text.trim() === "" && attachments.length === 0) return;
33072
+ this.#liveConfig = await this.#runtimeConfigStore()?.load();
32910
33073
  const admitted = await this.#admitAttachments(attachments);
32911
33074
  const runtime = await this.#ensureReady();
32912
33075
  const sessionId = this.#currentSessionId ?? (await this.createSession({ title: truncateTitle(text) })).id;
@@ -32934,12 +33097,36 @@ var ChatEngine = class {
32934
33097
  });
32935
33098
  const handle = this.#sessionHandle(runtime, sessionId);
32936
33099
  try {
32937
- const result = await handle.run(prompt);
33100
+ const uploadFiles = this.#uploadFilesFor(admitted.accepted);
33101
+ const result = await handle.run(prompt, uploadFiles ? { uploadFiles } : {});
32938
33102
  await this.#finishRun(sessionId, result);
32939
33103
  } catch (e) {
32940
33104
  this.#emitError(e);
32941
33105
  }
32942
33106
  }
33107
+ /**
33108
+ * 本轮触发消息里的附件,包成只对这一次 run 生效的读取端口(0.15.0 分册 17)。
33109
+ * 开关关着就返回 undefined → 脚本上下文里那两个成员整个不存在,
33110
+ * 与「本轮没传附件」(空数组)分得开。
33111
+ */
33112
+ #uploadFilesFor(attachments) {
33113
+ if (!(this.#liveConfig?.sandbox.uploadFiles ?? false)) return void 0;
33114
+ const files = attachments.map((a) => ({ ...a }));
33115
+ const infos = files.map((a) => ({
33116
+ id: a.id,
33117
+ name: a.name,
33118
+ contentType: a.contentType,
33119
+ size: a.size
33120
+ }));
33121
+ return {
33122
+ list: async () => infos,
33123
+ read: async (id) => {
33124
+ const attachment = files.find((a) => a.id === id);
33125
+ if (!attachment) throw new WebSkillError("UPLOAD_FILE_NOT_FOUND", `Upload file "${id}" is not part of the message that started this run`);
33126
+ return this.#adapter.storage.readBinary(`${this.attachmentsRoot}/${attachment.path}`);
33127
+ }
33128
+ };
33129
+ }
32943
33130
  /** 附件落盘根目录(composer 附件适配器写入,send 时读回) */
32944
33131
  get attachmentsRoot() {
32945
33132
  return `${this.#chatRoot}/attachments`;
@@ -32978,11 +33165,14 @@ var ChatEngine = class {
32978
33165
  if (!await this.#adapter.storage.exists(path)) continue;
32979
33166
  if (attachment.kind === "text" || attachment.kind === "document-text") {
32980
33167
  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
33168
  parts.push({
32984
33169
  type: "text",
32985
- text: `--- Attachment: ${label} ---\n${clipped}`
33170
+ text: formatAttachmentText({
33171
+ name: attachment.name,
33172
+ contentType: attachment.contentType,
33173
+ body,
33174
+ kind: attachment.kind
33175
+ })
32986
33176
  });
32987
33177
  continue;
32988
33178
  }
@@ -33082,6 +33272,13 @@ var ChatEngine = class {
33082
33272
  return this.#ready;
33083
33273
  }
33084
33274
  /**
33275
+ * `sandbox.downloadedFiles` 的实时取值(分册 15 · FR-15.6)。
33276
+ * 优先读每轮 `send()` 刷新的快照;还没发过消息时回落到装配期的那一份。
33277
+ */
33278
+ #sandboxDownloadedFiles(assembled) {
33279
+ return (this.#liveConfig ?? assembled)?.sandbox.downloadedFiles === true;
33280
+ }
33281
+ /**
33085
33282
  * LLM 选择链:options.llm(demo/测试)→ `RuntimeConfig.llm` 里选中/默认的条目
33086
33283
  * → 内置演示。streaming=false 时包装掉 stream(AgentLoop 走 complete)。
33087
33284
  */
@@ -33332,6 +33529,7 @@ var ChatEngine = class {
33332
33529
  })] : [],
33333
33530
  ...pagePerception ? [createPagePerceptionToolSource({
33334
33531
  policy: pagePerception,
33532
+ ...this.#adapter.pagePerceptionPaging === void 0 ? {} : { paging: () => this.#adapter.pagePerceptionPaging },
33335
33533
  imageCapture: async () => {
33336
33534
  const live = await this.#runtimeConfigStore()?.load();
33337
33535
  return {
@@ -33342,7 +33540,19 @@ var ChatEngine = class {
33342
33540
  };
33343
33541
  }
33344
33542
  })] : [],
33345
- ...this.#adapter.pageActions ? [createPageActionToolSource({ policy: this.#adapter.pageActions })] : []
33543
+ ...this.#adapter.pageActions ? [createPageActionToolSource({ policy: this.#adapter.pageActions })] : [],
33544
+ ...this.#adapter.downloads ? [createDownloadedFileToolSource({ policy: new DownloadedFilePolicy({
33545
+ reader: this.#adapter.downloads.reader,
33546
+ ui: this.#uiBridge,
33547
+ capabilityEnabled: () => this.#sandboxDownloadedFiles(rc),
33548
+ imageCapable: async () => {
33549
+ return entryCapabilities(pickLlmEntry(await this.#runtimeConfigStore()?.load(), this.#selectedModelId)).image;
33550
+ },
33551
+ ...this.#adapter.downloads.consent ? { consent: this.#adapter.downloads.consent } : {},
33552
+ ...this.#adapter.docxExtractor ? { docxExtractor: this.#adapter.docxExtractor } : {},
33553
+ ...this.#adapter.xlsxExtractor ? { xlsxExtractor: this.#adapter.xlsxExtractor } : {},
33554
+ ...this.#adapter.documentAudit ? { audit: this.#adapter.documentAudit } : {}
33555
+ }) })] : []
33346
33556
  ];
33347
33557
  const deps = {
33348
33558
  fs,
@@ -33377,11 +33587,15 @@ var ChatEngine = class {
33377
33587
  ...Object.keys(loopConfig).length > 0 ? { config: loopConfig } : {},
33378
33588
  ...model ? { model } : {},
33379
33589
  ...this.#options.fetchData ? { fetchData: this.#options.fetchData } : {},
33590
+ ...this.#options.dataSources ? { dataSources: this.#options.dataSources } : {},
33380
33591
  ...this.#adapter.linkedDocuments ? { linkedDocuments: this.#adapter.linkedDocuments } : {},
33381
33592
  ...this.#adapter.docxExtractor ? { docxExtractor: this.#adapter.docxExtractor } : {},
33382
33593
  ...this.#adapter.xlsxExtractor ? { xlsxExtractor: this.#adapter.xlsxExtractor } : {},
33383
33594
  ...this.#adapter.pdfExtractor ? { pdfExtractor: this.#adapter.pdfExtractor } : {},
33384
33595
  ...this.#adapter.documentAudit ? { documentAudit: this.#adapter.documentAudit } : {},
33596
+ ...this.#adapter.documentAudit ? { uploadFileAudit: this.#adapter.documentAudit } : {},
33597
+ maxUploadFileBytes: rc?.sandbox.maxUploadFileBytes ?? defaultRuntimeConfig().sandbox.maxUploadFileBytes,
33598
+ ...this.#adapter.documentSurface ? { documentSurface: true } : {},
33385
33599
  ...toolStepStore ? { toolSteps: toolStepStore } : {}
33386
33600
  };
33387
33601
  const runtime = new WebSkillRuntime({
@@ -33684,7 +33898,8 @@ function ChatbotSurfaceTexts({ children }) {
33684
33898
  arrayFirstItemOnly: t("surface.arrayFirstItemOnly"),
33685
33899
  readOnlySnapshot: t("surface.readOnlySnapshot"),
33686
33900
  suggested: t("surface.suggested"),
33687
- useSuggestion: t("surface.useSuggestion")
33901
+ useSuggestion: t("surface.useSuggestion"),
33902
+ openDocument: t("surface.openDocument")
33688
33903
  }), [t]),
33689
33904
  hostControlTexts: useMemo(() => ({
33690
33905
  previousPage: t("surface.control.previousPage"),
@@ -34656,33 +34871,10 @@ function AssistantUiEmptyState({ title, prompts, dynamicPrompts, limit, disabled
34656
34871
  });
34657
34872
  }
34658
34873
  /**
34659
- * 对话进行中的动态快捷指令条(FR-17.6)。
34660
- * 只接**动态**那一批:静态清单不随上下文变,常驻只会变成噪声。
34661
- * 空列表时整条不渲染,DOM 里不留空容器。
34874
+ * 对话进行中的动态快捷指令条(0.13.0 FR-17.6)已废止:
34875
+ * 每轮对话结束都在输入区上方冒出一条横向滚动的胶囊带,构成持续干扰。
34876
+ * `ChatbotConfig.dynamicQuickPrompts` 保留,但只在空态(`AssistantUiEmptyState`)生效。
34662
34877
  */
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
34878
 
34687
34879
  //#endregion
34688
34880
  //#region src/react/format.ts
@@ -45571,7 +45763,7 @@ function resolveByNonce(bridge, snapshots, event) {
45571
45763
  * 一组 surface 快照 → 当前渲染档的宿主(分册 17)。
45572
45764
  * 四档与原 `HistoricalSurfaces` 一一对应;差别只在 `bridge` 存在时补上动作/草稿回接。
45573
45765
  */
45574
- function SurfaceByRenderer({ snapshots, renderer, registry, bridge }) {
45766
+ function SurfaceByRenderer({ snapshots, renderer, registry, bridge, documentSurface }) {
45575
45767
  const drafts = bridge && snapshots[0]?.runId ? bridge.surfaceDrafts(snapshots[0].runId) : void 0;
45576
45768
  const onDraftChange = bridge ? (event) => bridge.setSurfaceDraft(event.runId, event.surfaceId, event.value) : void 0;
45577
45769
  if (renderer === "a2ui") {
@@ -45610,6 +45802,7 @@ function SurfaceByRenderer({ snapshots, renderer, registry, bridge }) {
45610
45802
  return /* @__PURE__ */ jsx(UiSurfaceSnapshotList, {
45611
45803
  snapshots,
45612
45804
  ...registry ? { registry } : {},
45805
+ ...documentSurface ? { documentSurface } : {},
45613
45806
  ...bridge ? { onAction: (event) => resolveByNonce(bridge, snapshots, event) } : {},
45614
45807
  ...drafts ? { drafts } : {},
45615
45808
  ...onDraftChange ? { onDraftChange } : {}
@@ -45644,6 +45837,7 @@ function InlineSurface({ surfaceId }) {
45644
45837
  snapshots: [snapshot],
45645
45838
  ...ctx.renderer ? { renderer: ctx.renderer } : {},
45646
45839
  ...ctx.registry ? { registry: ctx.registry } : {},
45840
+ ...ctx.documentSurface ? { documentSurface: ctx.documentSurface } : {},
45647
45841
  ...!persisted && bridge ? { bridge } : {}
45648
45842
  })
45649
45843
  });
@@ -46477,7 +46671,7 @@ function SkillBadges({ skills }) {
46477
46671
  //#endregion
46478
46672
  //#region src/react/WebSkillMessageExtensions.tsx
46479
46673
  /** WebSkill domain content rendered beneath a Base message without choosing its chat layout. */
46480
- function WebSkillMessageExtensions({ message, onDownload, surfaceRegistry, renderer }) {
46674
+ function WebSkillMessageExtensions({ message, onDownload, surfaceRegistry, documentSurface, renderer }) {
46481
46675
  const t = useT();
46482
46676
  if (message.role === "user") return null;
46483
46677
  const contentParts = message.contentParts ?? [];
@@ -46498,7 +46692,8 @@ function WebSkillMessageExtensions({ message, onDownload, surfaceRegistry, rende
46498
46692
  children: /* @__PURE__ */ jsx(SurfaceByRenderer, {
46499
46693
  snapshots: renderable,
46500
46694
  ...renderer ? { renderer } : {},
46501
- ...surfaceRegistry ? { registry: surfaceRegistry } : {}
46695
+ ...surfaceRegistry ? { registry: surfaceRegistry } : {},
46696
+ ...documentSurface ? { documentSurface } : {}
46502
46697
  })
46503
46698
  }) : null,
46504
46699
  renderable.length < surfaces.filter((snapshot) => !inlineIds.has(snapshot.id)).length ? /* @__PURE__ */ jsx("p", {
@@ -46625,6 +46820,7 @@ function AssistantUiMessage(props) {
46625
46820
  ...props.bridge ? { bridge: props.bridge } : {},
46626
46821
  ...props.renderer ? { renderer: props.renderer } : {},
46627
46822
  ...props.surfaceRegistry ? { registry: props.surfaceRegistry } : {},
46823
+ ...props.documentSurface ? { documentSurface: props.documentSurface } : {},
46628
46824
  ...props.onDownload ? { onDownload: props.onDownload } : {}
46629
46825
  }), [
46630
46826
  message?.surfaces,
@@ -46633,6 +46829,7 @@ function AssistantUiMessage(props) {
46633
46829
  props.bridge,
46634
46830
  props.renderer,
46635
46831
  props.surfaceRegistry,
46832
+ props.documentSurface,
46636
46833
  props.onDownload
46637
46834
  ]);
46638
46835
  if (role === "user") return /* @__PURE__ */ jsxs("div", {
@@ -46688,6 +46885,7 @@ function AssistantUiMessage(props) {
46688
46885
  message,
46689
46886
  ...props.onDownload ? { onDownload: props.onDownload } : {},
46690
46887
  ...props.surfaceRegistry ? { surfaceRegistry: props.surfaceRegistry } : {},
46888
+ ...props.documentSurface ? { documentSurface: props.documentSurface } : {},
46691
46889
  ...props.renderer ? { renderer: props.renderer } : {}
46692
46890
  }) : null,
46693
46891
  /* @__PURE__ */ jsx(AssistantUiMessageActions, { ...props })
@@ -46697,52 +46895,6 @@ function AssistantUiMessage(props) {
46697
46895
 
46698
46896
  //#endregion
46699
46897
  //#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
46898
  const TEXT_ACCEPT = [
46747
46899
  "text/plain",
46748
46900
  "text/markdown",
@@ -47264,18 +47416,43 @@ function pageActionDetails(details) {
47264
47416
  const record = details;
47265
47417
  const action = record["action"];
47266
47418
  const target = record["target"];
47267
- if (action !== "click" && action !== "fill" && action !== "submit") return void 0;
47419
+ if (typeof action !== "string" || !PAGE_ACTION_KINDS.includes(action)) return void 0;
47268
47420
  if (typeof target !== "object" || target === null) return void 0;
47269
47421
  const { role, name, frame } = target;
47270
47422
  if (typeof role !== "string") return void 0;
47271
47423
  const value = record["value"];
47424
+ const rememberLabel = record["rememberLabel"];
47272
47425
  return {
47273
47426
  action,
47274
47427
  role,
47275
47428
  ...typeof name === "string" ? { name } : {},
47276
47429
  ...typeof value === "string" ? { value } : {},
47277
47430
  ...typeof frame === "string" ? { frame } : {},
47278
- ...record["elevated"] === true ? { elevated: true } : {}
47431
+ ...record["elevated"] === true ? { elevated: true } : {},
47432
+ ...record["rememberable"] === true ? { rememberable: true } : {},
47433
+ ...typeof rememberLabel === "string" ? { rememberLabel } : {}
47434
+ };
47435
+ }
47436
+ /**
47437
+ * 下载文件确认卡的载荷(0.14.0 分册 20)。与 `pageActionDetails` 同一条规矩:
47438
+ * 形状不对就整段不渲染,退回通用授权展示,不猜。
47439
+ */
47440
+ function downloadedFileDetails(details) {
47441
+ if (typeof details !== "object" || details === null) return void 0;
47442
+ const record = details;
47443
+ const action = record["action"];
47444
+ if (action !== "list" && action !== "read") return void 0;
47445
+ const file = record["file"];
47446
+ const rememberLabel = record["rememberLabel"];
47447
+ const named = typeof file === "object" && file !== null ? file : void 0;
47448
+ return {
47449
+ action,
47450
+ ...typeof named?.["name"] === "string" && typeof named["size"] === "number" ? { file: {
47451
+ name: named["name"],
47452
+ size: named["size"]
47453
+ } } : {},
47454
+ ...record["rememberable"] === true ? { rememberable: true } : {},
47455
+ ...typeof rememberLabel === "string" ? { rememberLabel } : {}
47279
47456
  };
47280
47457
  }
47281
47458
  /**
@@ -47308,8 +47485,10 @@ function traceEvidenceDetails(details) {
47308
47485
  }
47309
47486
  function ActiveInteractionCard({ request, bridge }) {
47310
47487
  const t = useT();
47488
+ const [remember, setRemember] = useState(false);
47311
47489
  const isAuthorize = request.type === "authorize";
47312
47490
  const pageAction = isAuthorize && request.capability === "pageAction" ? pageActionDetails(request.details) : void 0;
47491
+ const downloadedFile = isAuthorize && request.capability === "readDownloadedFile" ? downloadedFileDetails(request.details) : void 0;
47313
47492
  const traceEvidence = isAuthorize ? traceEvidenceDetails(request.details) : void 0;
47314
47493
  const spec = interactionToUiSpec(request.type === "authorize" && pageAction !== void 0 ? {
47315
47494
  ...request,
@@ -47337,7 +47516,8 @@ function ActiveInteractionCard({ request, bridge }) {
47337
47516
  const value = shapeInteractionValue(interactionToFormModel(request), event.value ?? {});
47338
47517
  bridge.resolve({
47339
47518
  id: request.id,
47340
- value
47519
+ value,
47520
+ ...remember ? { remembered: true } : {}
47341
47521
  });
47342
47522
  };
47343
47523
  return /* @__PURE__ */ jsxs(motion.div, {
@@ -47413,8 +47593,33 @@ function ActiveInteractionCard({ request, bridge }) {
47413
47593
  "data-testid": "interaction-page-action-elevated",
47414
47594
  className: "text-sm font-medium text-warning",
47415
47595
  children: t("interaction.pageAction.elevated")
47596
+ }) : null,
47597
+ pageAction.rememberable === true ? /* @__PURE__ */ jsxs("label", {
47598
+ className: "flex items-center gap-2 text-sm text-ink",
47599
+ children: [/* @__PURE__ */ jsx("input", {
47600
+ type: "checkbox",
47601
+ "data-testid": "interaction-page-action-remember",
47602
+ checked: remember,
47603
+ onChange: (event) => setRemember(event.currentTarget.checked)
47604
+ }), pageAction.rememberLabel ?? t("interaction.pageAction.remember")]
47416
47605
  }) : null
47417
47606
  ] }) : null,
47607
+ downloadedFile ? /* @__PURE__ */ jsxs(Fragment$1, { children: [downloadedFile.file ? /* @__PURE__ */ jsx("p", {
47608
+ "data-testid": "interaction-downloaded-file",
47609
+ className: "text-sm text-ink",
47610
+ children: t("interaction.downloadedFile.file", {
47611
+ name: downloadedFile.file.name,
47612
+ size: formatBytes(downloadedFile.file.size)
47613
+ })
47614
+ }) : null, downloadedFile.rememberable === true ? /* @__PURE__ */ jsxs("label", {
47615
+ className: "flex items-center gap-2 text-sm text-ink",
47616
+ children: [/* @__PURE__ */ jsx("input", {
47617
+ type: "checkbox",
47618
+ "data-testid": "interaction-downloaded-file-remember",
47619
+ checked: remember,
47620
+ onChange: (event) => setRemember(event.currentTarget.checked)
47621
+ }), downloadedFile.rememberLabel ?? t("interaction.downloadedFile.remember")]
47622
+ }) : null] }) : null,
47418
47623
  traceEvidence ? /* @__PURE__ */ jsxs("div", {
47419
47624
  "data-testid": "interaction-trace-evidence",
47420
47625
  children: [/* @__PURE__ */ jsx("div", {
@@ -47894,6 +48099,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
47894
48099
  ...config?.governance ? { governance: config.governance } : {},
47895
48100
  ...config?.skillCandidates ? { skillCandidates: config.skillCandidates } : {},
47896
48101
  ...config?.fetchData ? { fetchData: config.fetchData } : {},
48102
+ ...config?.dataSources ? { dataSources: config.dataSources } : {},
48103
+ ...config?.executorFactory ? { executorFactory: config.executorFactory } : {},
47897
48104
  ...rendererProp ? { renderer: rendererProp } : {}
47898
48105
  }), [
47899
48106
  adapter,
@@ -47906,6 +48113,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
47906
48113
  config?.governance,
47907
48114
  config?.skillCandidates,
47908
48115
  config?.fetchData,
48116
+ config?.dataSources,
48117
+ config?.executorFactory,
47909
48118
  rendererProp
47910
48119
  ]);
47911
48120
  useEffect(() => {
@@ -48089,7 +48298,9 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
48089
48298
  unsubscribe?.();
48090
48299
  };
48091
48300
  }, [configStore, engine]);
48301
+ const hasBuiltinEntry = llmSelection.entries.some((entry) => entry.provider === "chrome-builtin");
48092
48302
  useEffect(() => {
48303
+ if (!hasBuiltinEntry) return void 0;
48093
48304
  let cancelled = false;
48094
48305
  probeChromeBuiltinAvailability().then((result) => {
48095
48306
  if (!cancelled) setChromeBuiltin(result);
@@ -48097,7 +48308,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
48097
48308
  return () => {
48098
48309
  cancelled = true;
48099
48310
  };
48100
- }, []);
48311
+ }, [hasBuiltinEntry]);
48101
48312
  /** 选中模型条目 id:能力受限提示、设置面板置灰与附件门槛都以它为准 */
48102
48313
  const selectedModelId = modelId ?? llmSelection.defaultId ?? llmSelection.entries[0]?.id ?? "";
48103
48314
  const selectedEntry = llmSelection.entries.find((entry) => entry.id === selectedModelId);
@@ -48514,6 +48725,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
48514
48725
  modelLabels,
48515
48726
  resolveAttachmentUrl,
48516
48727
  ...surfaceRegistry ? { surfaceRegistry } : {},
48728
+ ...engine.documentSurface ? { documentSurface: engine.documentSurface } : {},
48517
48729
  renderer,
48518
48730
  bridge: engine.bridge,
48519
48731
  ...adapter.navigation.openConsoleTrace ? { onViewTrace: adapter.navigation.openConsoleTrace } : {},
@@ -48557,11 +48769,6 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
48557
48769
  } : {},
48558
48770
  onLocate: handleLocateInteraction
48559
48771
  }),
48560
- messages.length > 0 && !sending && config?.dynamicQuickPrompts ? /* @__PURE__ */ jsx(AssistantUiDynamicPrompts, {
48561
- prompts: config.dynamicQuickPrompts,
48562
- limit: quickPromptLimit,
48563
- onPrompt: handleSend
48564
- }) : null,
48565
48772
  /* @__PURE__ */ jsx(AssistantUiComposer, {
48566
48773
  ...modelCapabilities ? { capabilities: {
48567
48774
  tools: modelCapabilities.tools,
@@ -48654,7 +48861,7 @@ function VercelPayloadPreview({ bridge }) {
48654
48861
  * Version of the published `@webskill/chatbot` package, injected at build time.
48655
48862
  * @stable
48656
48863
  */
48657
- const CHATBOT_VERSION = "0.10.0";
48864
+ const CHATBOT_VERSION = "0.12.0";
48658
48865
 
48659
48866
  //#endregion
48660
48867
  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 };