@webskill/chatbot 0.24.3 → 0.25.1

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/chatbot.css CHANGED
@@ -1204,6 +1204,9 @@
1204
1204
  .pb-1 {
1205
1205
  padding-bottom: var(--spacing);
1206
1206
  }
1207
+ .pb-2 {
1208
+ padding-bottom: calc(var(--spacing) * 2);
1209
+ }
1207
1210
  .pb-3 {
1208
1211
  padding-bottom: calc(var(--spacing) * 3);
1209
1212
  }
@@ -1867,6 +1870,11 @@
1867
1870
  color: var(--sidebar-accent-foreground);
1868
1871
  }
1869
1872
  }
1873
+ @media (prefers-reduced-motion: reduce) {
1874
+ .motion-reduce\:animate-none {
1875
+ animation: none;
1876
+ }
1877
+ }
1870
1878
  @media (width < 40rem) {
1871
1879
  .max-sm\:top-0 {
1872
1880
  top: 0px;
package/dist/index.d.ts CHANGED
@@ -340,7 +340,7 @@ interface ChatbotHostAdapter {
340
340
  /** xlsx → 行流(`createXlsxBlockReader`);不注入即 `read_document` 读不了 Excel @experimental */
341
341
  xlsxDocumentReader?: XlsxDocumentReader;
342
342
  /**
343
- * xlsx → 单元格网格(`createXlsxTableReader`)。注入后,估算超过阈值的 xlsx 附件只以画像进消息,
343
+ * xlsx → 单元格网格或可重复扫描的行流(`createXlsxTableReader`)。注入后,估算超过阈值的 xlsx 附件只以画像进消息,
344
344
  * 统计交给 `query_table` 在本机对全表计算(整张表不进上下文);不注入则两者都不发生。
345
345
  * @experimental
346
346
  */
@@ -1632,6 +1632,7 @@ declare const chatbotDictionary: {
1632
1632
  'composer.inputLabel': string;
1633
1633
  'composer.send': string;
1634
1634
  'composer.stop': string;
1635
+ 'composer.processingAttachments': string;
1635
1636
  'composer.attachments': string;
1636
1637
  'composer.addAttachment': string;
1637
1638
  'composer.removeAttachment': string;
@@ -2092,6 +2093,7 @@ declare const chatbotDictionary: {
2092
2093
  'composer.inputLabel': string;
2093
2094
  'composer.send': string;
2094
2095
  'composer.stop': string;
2096
+ 'composer.processingAttachments': string;
2095
2097
  'composer.attachments': string;
2096
2098
  'composer.addAttachment': string;
2097
2099
  'composer.removeAttachment': string;
@@ -2555,6 +2557,7 @@ declare const useT: () => TranslateFn<{
2555
2557
  'composer.inputLabel': string;
2556
2558
  'composer.send': string;
2557
2559
  'composer.stop': string;
2560
+ 'composer.processingAttachments': string;
2558
2561
  'composer.attachments': string;
2559
2562
  'composer.addAttachment': string;
2560
2563
  'composer.removeAttachment': string;
@@ -3015,6 +3018,7 @@ declare const useT: () => TranslateFn<{
3015
3018
  'composer.inputLabel': string;
3016
3019
  'composer.send': string;
3017
3020
  'composer.stop': string;
3021
+ 'composer.processingAttachments': string;
3018
3022
  'composer.attachments': string;
3019
3023
  'composer.addAttachment': string;
3020
3024
  'composer.removeAttachment': string;
@@ -3494,6 +3498,6 @@ declare function ErrorCard({ code, message, loopLimits, onOpenSettings, onDismis
3494
3498
  * Version of the published `@webskill/chatbot` package, injected at build time.
3495
3499
  * @stable
3496
3500
  */
3497
- declare const CHATBOT_VERSION = "0.24.3";
3501
+ declare const CHATBOT_VERSION = "0.25.1";
3498
3502
  //#endregion
3499
3503
  export { A2uiSurfaceHost, type A2uiSurfaceHostProps, A2uiSurfaceSnapshotHost, type A2uiSurfaceSnapshotHostProps, type AppearanceChange, CHATBOT_VERSION, type ChatAttachmentInput, type ChatAttachmentMeta, type ChatComponent, type ChatComponentCallRecord, type ChatComponentConfirmation, type ChatComponentCredentialField, type ChatComponentInvokeContext, type ChatComponentOutcome, type ChatComponentStore, type ChatComponentText, type ChatContentPart, ChatEngine, type ChatEngineOptions, type ChatEvent, type ChatInteractionRecord, type ChatLayout, type ChatMessage, type ChatRunPhase, type ChatSessionListResult, type ChatSessionMeta, type ChatTheme, type ChatThinking, type ChatTokenUsage, type ChatToolCall, Chatbot, type ChatbotConfig, type ChatbotGovernancePorts, type ChatbotHostAdapter, type ChatbotProps, ChatbotVisionDelegate, type ChatbotVisionDelegateOptions, CompositeUiBridge, type CompositeUiBridgeDeps, DEFAULT_RENDERER_CAPABILITIES, DEFAULT_RUNTIME_CONFIG_STORAGE_KEY, type DownloadableFile, type DwgHost, ErrorCard, type ErrorCardProps, type HttpMessageComponentOptions, InteractionCard, type InteractionCardProps, InterruptedBanner, type InterruptedBannerProps, type Locale, type LocalizedText, OpenUiSurfaceHost, type OpenUiSurfaceHostProps, OpenUiSurfaceSnapshotHost, type OpenUiSurfaceSnapshotHostProps, type QuickPrompt, type QuickPromptIconName, type RendererCapability, type RendererKind, type ResolvedChatLayout, ResultBlockList, type ResultBlockListProps, ResultBlocksPro, type ResultBlocksProProps, type RunSnapshot, type RuntimeAppearanceConfig, type RuntimeConfig, type RuntimeConfigStore, type RuntimeQuickPrompt, type RuntimeRendererId, type SandboxExecutorDeps, type SettingsSectionId, SkillBadges, SpecInteraction, type SpecInteractionProps, VercelPayloadPreview, type VercelPayloadPreviewProps, VercelSurfaceHost, type VercelSurfaceHostProps, VercelSurfaceSnapshotHost, type VercelSurfaceSnapshotHostProps, type VisionDelegationConsent, type VisionDelegationEndpoint, WEB_OFFICE_TEMPLATE_PREFIX, chatbotDictionary, configureA2uiMarkdown, createHttpMessageComponent, createLocalStorageRuntimeConfigStore, createMemoryRuntimeConfigStore, isLlmEntryUsable, pickUsableLlmEntry, probeA2uiAvailability, probeOpenUiAvailability, sandboxExecutorDeps, useT };
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import { ActionBarPrimitive, AttachmentPrimitive, AuiProvider, ComposerPrimitive
23
23
  import { BrowserWorkerScriptExecutor, WebOfficePolicy, capturePhoto, checkCameraAvailability, checkDictationAvailability, compressImageToBudget, createEncryptedMemoryStore, createLlmClient, createWebOfficeToolSource, deleteMemoryEncryptionKey, openCamera, openMemoryEncryptionKey, probeChromeBuiltinAvailability, startDictation, toDocumentImage } from "@webskill/sdk/browser";
24
24
  import { JsonRenderSpecSurface, NativeSpecSurface, OpenUiSpecSurface, ReactBridgeState, SpecInteractionChannel, SurfaceFormTextsProvider, UiSurfaceSnapshotList, probeJsonRenderAvailability } from "@webskill/sdk/ui-react";
25
25
  import { A2UI_SPEC_FORM_PATH, collectFormScopes, createUiCatalogToolSource, fromA2uiSpecAction, fromA2uiSurfaceAction, interactionToFormModel, interactionToUiSpec, loadOpenUiPeers, loadWebSkillLitCatalog, renderMiniChart, shapeInteractionValue, toA2uiSpecMessages, toA2uiSurfaceAction, toVercelToolInvocation, uiCatalog } from "@webskill/sdk/ui";
26
- import { DownloadedFilePolicy, PAGE_ACTION_KINDS, SCROLL_DIRECTIONS, TodoStore, createDelegationToolSource, createDocumentToolSource, createDownloadedFileToolSource, createDwgToolSource, createPageActionToolSource, createPageImageToolSource, createPagePerceptionToolSource, createRemoteImageToolSource, createSkillGenerationToolSource, createSpreadsheetToolSource, createTabWorksetToolSource, createTableQueryToolSource, createTemplateToolSource, createTodoToolSource, profileTable, sniffDocumentFormat, withDelegationOrigin } from "@webskill/sdk/agent";
26
+ import { DownloadedFilePolicy, PAGE_ACTION_KINDS, SCROLL_DIRECTIONS, TodoStore, createDelegationToolSource, createDocumentToolSource, createDownloadedFileToolSource, createDwgToolSource, createPageActionToolSource, createPageImageToolSource, createPagePerceptionToolSource, createRemoteImageToolSource, createSkillGenerationToolSource, createSpreadsheetToolSource, createTabWorksetToolSource, createTableQueryToolSource, createTemplateToolSource, createTodoToolSource, isStreamingTableWorkbook, profileStreamingTable, profileTable, sniffDocumentFormat, summarizeStreamingWorkbook, withDelegationOrigin } from "@webskill/sdk/agent";
27
27
  import { DataRenderers, Tools } from "@assistant-ui/core/react";
28
28
  import { ModelContext, Suggestions } from "@assistant-ui/core/store";
29
29
  import { Derived, attachTransformScopes, useClientLookup, useClientResource } from "@assistant-ui/store";
@@ -25916,6 +25916,7 @@ const chatbotDictionary = defineDictionary({
25916
25916
  "composer.inputLabel": "Message input",
25917
25917
  "composer.send": "Send",
25918
25918
  "composer.stop": "Stop",
25919
+ "composer.processingAttachments": "Processing attachments on this device. Large files may take a while; please keep this chat open…",
25919
25920
  "composer.attachments": "Attachments",
25920
25921
  "composer.addAttachment": "Attach a file",
25921
25922
  "composer.removeAttachment": "Remove attachment: {name}",
@@ -26376,6 +26377,7 @@ const chatbotDictionary = defineDictionary({
26376
26377
  "composer.inputLabel": "消息输入框",
26377
26378
  "composer.send": "发送",
26378
26379
  "composer.stop": "停止",
26380
+ "composer.processingAttachments": "正在本机处理附件,大文件可能需要一些时间,请保持对话打开…",
26379
26381
  "composer.attachments": "附件",
26380
26382
  "composer.addAttachment": "添加附件",
26381
26383
  "composer.removeAttachment": "移除附件:{name}",
@@ -27918,7 +27920,7 @@ function smallTableHint(id) {
27918
27920
  * 已解析的表按「会话 + 来源」缓存(FR-10.17)。
27919
27921
  *
27920
27922
  * 缓存的是进行中的 promise:分流时解析一次,紧接着的查询复用同一份;失败不留。
27921
- * 容量 2、最近使用淘汰——一张 2 万 × 12 的网格在堆里是十余 MB,
27923
+ * 容量 2、最近使用淘汰——网格工作簿会占堆内存,流式工作簿只缓存元数据与取件出口,
27922
27924
  * 留两格是为「同一会话里先后传两份表做对比」。
27923
27925
  */
27924
27926
  var TableCache = class {
@@ -29474,33 +29476,56 @@ var ChatEngine = class {
29474
29476
  if (extract === void 0) return void 0;
29475
29477
  return await extract(bytes);
29476
29478
  }
29479
+ async #readTable(reader, path) {
29480
+ if (reader.readStreaming !== void 0) {
29481
+ const storage = this.#adapter.storage;
29482
+ if (storage.createReadStream !== void 0) return await reader.readStreaming(() => storage.createReadStream(path));
29483
+ return await reader.readStreaming(await storage.readBinary(path));
29484
+ }
29485
+ return await reader.read(await this.#adapter.storage.readBinary(path));
29486
+ }
29477
29487
  /**
29478
29488
  * xlsx 附件的发送形态分流(0.26.0 分册 10 · FR-10.1 / FR-10.5)。
29479
29489
  *
29480
29490
  * 返回 `undefined` = 不适用(没接端口、不是 xlsx、解析失败),调用方走 0.25.0 的原路径——
29481
29491
  * 解析失败时同一份坏文件会在原路径里以既有的形态报错,这里不新增失败形态。
29482
- * 解析在这里先做一次、网格进缓存,随后的 `query_table` 复用同一份。
29492
+ * 解析在这里先做一次、工作簿进缓存,随后的 `query_table` 复用同一份。
29483
29493
  */
29484
29494
  async #tableRouting(attachment, path, sessionId) {
29485
29495
  const reader = this.#adapter.xlsxTableReader;
29486
29496
  if (reader === void 0 || attachment.kind !== "document-text") return void 0;
29487
29497
  if (extractedDocumentFormat(attachment.contentType, attachment.name) !== "xlsx") return void 0;
29488
- const workbook = await this.#tables.get(sessionId, `att:${attachment.path}`, async () => reader.read(await this.#adapter.storage.readBinary(path))).catch(() => void 0);
29498
+ const workbook = await this.#tables.get(sessionId, `att:${attachment.path}`, async () => this.#readTable(reader, path)).catch(() => void 0);
29489
29499
  if (workbook === void 0) return void 0;
29490
- const tokens = estimateTableTokens(workbook);
29500
+ let tokens;
29501
+ let rows;
29502
+ let profile;
29503
+ if (isStreamingTableWorkbook(workbook)) {
29504
+ const summary = await summarizeStreamingWorkbook(workbook);
29505
+ tokens = summary.tokens;
29506
+ rows = summary.dataRows;
29507
+ profile = () => profileStreamingTable({
29508
+ name: attachment.name,
29509
+ id: attachment.id,
29510
+ workbook
29511
+ });
29512
+ } else {
29513
+ tokens = estimateTableTokens(workbook);
29514
+ rows = countDataRows(workbook);
29515
+ profile = () => Promise.resolve(profileTable({
29516
+ name: attachment.name,
29517
+ id: attachment.id,
29518
+ workbook
29519
+ }));
29520
+ }
29491
29521
  if (tokens <= 24e3) return {
29492
29522
  kind: "inline",
29493
29523
  hint: smallTableHint(attachment.id)
29494
29524
  };
29495
- const rows = countDataRows(workbook);
29496
29525
  const locale = this.#liveConfig?.appearance.locale ?? "en";
29497
29526
  return {
29498
29527
  kind: "profile",
29499
- profile: profileTable({
29500
- name: attachment.name,
29501
- id: attachment.id,
29502
- workbook
29503
- }),
29528
+ profile: await profile(),
29504
29529
  steering: tableQuerySteering({
29505
29530
  id: attachment.id,
29506
29531
  tokens,
@@ -29523,8 +29548,22 @@ var ChatEngine = class {
29523
29548
  async #downloadSheetRouting(input) {
29524
29549
  const reader = this.#adapter.xlsxTableReader;
29525
29550
  if (reader === void 0) return void 0;
29526
- const workbook = await this.#tables.get(this.#currentSessionId ?? "", `dl:${input.entry.id}`, () => reader.read(input.bytes)).catch(() => void 0);
29551
+ const workbook = await this.#tables.get(this.#currentSessionId ?? "", `dl:${input.entry.id}`, () => reader.readStreaming !== void 0 ? reader.readStreaming(input.bytes) : reader.read(input.bytes)).catch(() => void 0);
29527
29552
  if (workbook === void 0) return void 0;
29553
+ if (isStreamingTableWorkbook(workbook)) {
29554
+ const summary = await summarizeStreamingWorkbook(workbook);
29555
+ if (summary.tokens <= 24e3 && summary.textChars <= ATTACHMENT_TEXT_LIMIT) return void 0;
29556
+ return `${await profileStreamingTable({
29557
+ name: input.entry.name,
29558
+ id: input.entry.id,
29559
+ workbook
29560
+ })}\n\n` + tableQuerySteering({
29561
+ id: input.entry.id,
29562
+ tokens: summary.tokens,
29563
+ rows: summary.dataRows,
29564
+ download: true
29565
+ });
29566
+ }
29528
29567
  const tokens = estimateTableTokens(workbook);
29529
29568
  if (tokens <= 24e3 && tableTextChars(workbook) <= ATTACHMENT_TEXT_LIMIT) return void 0;
29530
29569
  return `${profileTable({
@@ -29554,7 +29593,7 @@ var ChatEngine = class {
29554
29593
  const notXlsx = (name) => new WebSkillError("TOOL_UNSUPPORTED", `"${name}" is not an Excel (.xlsx) workbook; query_table reads .xlsx only.`);
29555
29594
  const fromAttachment = async (a) => {
29556
29595
  if (a.kind !== "document-text" || extractedDocumentFormat(a.contentType, a.name) !== "xlsx") throw notXlsx(a.name);
29557
- const workbook = await this.#tables.get(sessionId, `att:${a.path}`, async () => reader.read(await this.#adapter.storage.readBinary(`${this.attachmentsRoot}/${a.path}`)));
29596
+ const workbook = await this.#tables.get(sessionId, `att:${a.path}`, async () => this.#readTable(reader, `${this.attachmentsRoot}/${a.path}`));
29558
29597
  return {
29559
29598
  name: a.name,
29560
29599
  workbook
@@ -29575,7 +29614,7 @@ var ChatEngine = class {
29575
29614
  if (sniffDocumentFormat(bytes, name) !== "xlsx") throw notXlsx(name);
29576
29615
  return {
29577
29616
  name,
29578
- workbook: await this.#tables.get(sessionId, source, () => reader.read(bytes))
29617
+ workbook: await this.#tables.get(sessionId, source, () => reader.readStreaming !== void 0 ? reader.readStreaming(bytes) : reader.read(bytes))
29579
29618
  };
29580
29619
  };
29581
29620
  const stored = context?.binaries?.get(id);
@@ -30266,7 +30305,16 @@ var ChatEngine = class {
30266
30305
  }),
30267
30306
  ...this.#adapter.xlsxTableReader ? [createTableQueryToolSource({
30268
30307
  tables: { load: (id, context) => this.#loadTable(id, context, downloadPolicy) },
30269
- downloadedFiles: () => downloadPolicy?.enabled === true
30308
+ downloadedFiles: () => downloadPolicy?.enabled === true,
30309
+ writeArtifact: (path, content, mimeType, context) => {
30310
+ if (!context?.runId) throw new WebSkillError("TOOL_EXECUTION_FAILED", "Writing a query result requires an active run.");
30311
+ return artifactStore.createBinaryArtifact({
30312
+ runId: context.runId,
30313
+ path,
30314
+ content,
30315
+ mimeType
30316
+ });
30317
+ }
30270
30318
  })] : [],
30271
30319
  ...this.#adapter.xlsxTemplateEngine || this.#adapter.docxTemplateEngine ? [createTemplateToolSource({
30272
30320
  files: { read: readTemplateBytes },
@@ -32442,7 +32490,7 @@ function ModelMenuButton({ models }) {
32442
32490
  }, option.id))
32443
32491
  })] });
32444
32492
  }
32445
- function AssistantUiComposer({ running, disabled = false, waitTarget, placeholder, attachments = false, attachmentInfo, attachmentImages, centered = false, models, noModel, dictationLang, onDictationPermissionDenied, onCameraPermissionDenied, capabilities }) {
32493
+ function AssistantUiComposer({ running, disabled = false, processingAttachments = false, waitTarget, placeholder, attachments = false, attachmentInfo, attachmentImages, centered = false, models, noModel, dictationLang, onDictationPermissionDenied, onCameraPermissionDenied, capabilities }) {
32446
32494
  const t = useT();
32447
32495
  const locale = useLocale();
32448
32496
  const dictation = useComposerDictation(dictationLang ?? DEFAULT_DICTATION_LANG[locale], onDictationPermissionDenied);
@@ -32459,11 +32507,20 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
32459
32507
  const cameraReason = !attachments ? t("composer.camera.noStorage") : attachmentReason ?? (camera.availability.available ? void 0 : t(`composer.camera.${camera.availability.reason}`));
32460
32508
  const cameraFailure = camera.failure === void 0 ? void 0 : camera.failure === "denied" && onCameraPermissionDenied !== void 0 ? t("composer.camera.denied.handoff") : t(`composer.camera.${camera.failure}`);
32461
32509
  const plainChatHintId = useId();
32462
- return /* @__PURE__ */ jsx(ComposerPrimitive.Root, {
32510
+ return /* @__PURE__ */ jsxs(ComposerPrimitive.Root, {
32463
32511
  "data-testid": "assistant-ui-composer",
32464
32512
  "data-centered": centered,
32465
32513
  className: "webskill-aui-composer shrink-0 px-4 py-4",
32466
- children: /* @__PURE__ */ jsxs(ComposerPrimitive.AttachmentDropzone, {
32514
+ children: [processingAttachments ? /* @__PURE__ */ jsxs("div", {
32515
+ "data-testid": "attachment-processing",
32516
+ role: "status",
32517
+ "aria-live": "polite",
32518
+ className: "webskill-chat-prose mx-auto flex w-full items-center gap-2 px-3 pb-2 text-xs text-muted",
32519
+ children: [/* @__PURE__ */ jsx(LoaderCircle, {
32520
+ className: "size-3.5 shrink-0 animate-spin motion-reduce:animate-none",
32521
+ "aria-hidden": true
32522
+ }), /* @__PURE__ */ jsx("span", { children: t("composer.processingAttachments") })]
32523
+ }) : null, /* @__PURE__ */ jsxs(ComposerPrimitive.AttachmentDropzone, {
32467
32524
  disabled: !attachments,
32468
32525
  className: "webskill-aui-composer-shell webskill-chat-prose mx-auto flex w-full flex-col gap-2 px-3 py-2 transition-colors data-[dragging=true]:border-ring",
32469
32526
  children: [
@@ -32689,7 +32746,7 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
32689
32746
  onCancel: camera.cancel
32690
32747
  })
32691
32748
  ]
32692
- })
32749
+ })]
32693
32750
  });
32694
32751
  }
32695
32752
 
@@ -42980,6 +43037,22 @@ function fallbackReason(error) {
42980
43037
  const code = error?.code;
42981
43038
  return typeof code === "string" && code !== "" ? code : "a2ui-render-failed";
42982
43039
  }
43040
+ function formControlValues(root) {
43041
+ const values = {};
43042
+ const visit = (node) => {
43043
+ for (const child of node.children) {
43044
+ if (child instanceof HTMLInputElement) {
43045
+ if (child.id) values[child.id] = child.type === "checkbox" ? child.checked : child.type === "number" ? Number(child.value) : child.value;
43046
+ } else if (child instanceof HTMLTextAreaElement || child instanceof HTMLSelectElement) {
43047
+ if (child.id) values[child.id] = child instanceof HTMLSelectElement && child.multiple ? [...child.selectedOptions].map((option) => option.value) : child.value;
43048
+ }
43049
+ if (child.shadowRoot) visit(child.shadowRoot);
43050
+ else visit(child);
43051
+ }
43052
+ };
43053
+ if (root) visit(root.shadowRoot ?? root);
43054
+ return values;
43055
+ }
42983
43056
  /**
42984
43057
  * `a2ui` 档:catalog 声明树经 BYOC catalog 的 Lit 元素渲染。
42985
43058
  * 元素与协议里公布的 `catalog_definition` 派生自同一份 shape,因此这里
@@ -42990,6 +43063,7 @@ function fallbackReason(error) {
42990
43063
  */
42991
43064
  function A2uiSpecSurface({ spec, surfaceId, actions, onAction, registry, runId, draft, onDraftChange }) {
42992
43065
  const mountRef = useRef(null);
43066
+ const formValuesRef = useRef({});
42993
43067
  const [fallback, setFallback] = useState();
42994
43068
  const emit = useEffectEvent((actionId, value) => {
42995
43069
  const declared = (actions ?? []).find((action) => action.id === actionId);
@@ -43019,8 +43093,11 @@ function A2uiSpecSurface({ spec, surfaceId, actions, onAction, registry, runId,
43019
43093
  const processor = new MessageProcessor([catalog, lit.basicCatalog], (action) => {
43020
43094
  const decoded = fromA2uiSpecAction(action);
43021
43095
  if (!decoded) return;
43022
- const values = processor.getClientDataModel("v0.9.1")?.surfaces?.[a2uiSurfaceId]?.[A2UI_SPEC_FORM_PATH.slice(1)];
43023
- emit(decoded.actionId, values ?? {});
43096
+ const values = {
43097
+ ...processor.getClientDataModel("v0.9.1")?.surfaces?.[a2uiSurfaceId]?.[A2UI_SPEC_FORM_PATH.slice(1)],
43098
+ ...formControlValues(surfaceElement)
43099
+ };
43100
+ emit(decoded.actionId, values);
43024
43101
  }, { version: "v0.9.1" });
43025
43102
  processor.onSurfaceCreated((surface) => {
43026
43103
  if (disposed) return;
@@ -43029,6 +43106,21 @@ function A2uiSpecSurface({ spec, surfaceId, actions, onAction, registry, runId,
43029
43106
  mount.replaceChildren(surfaceElement);
43030
43107
  });
43031
43108
  processor.processMessages(toA2uiSpecMessages(uiCatalog, spec, { surfaceId: a2uiSurfaceId }));
43109
+ const previousValues = formValuesRef.current;
43110
+ if (Object.keys(previousValues).length > 0) {
43111
+ const defaults = processor.getClientDataModel("v0.9.1")?.surfaces?.[a2uiSurfaceId]?.[A2UI_SPEC_FORM_PATH.slice(1)];
43112
+ processor.processMessages([{
43113
+ version: "v0.9.1",
43114
+ updateDataModel: {
43115
+ surfaceId: a2uiSurfaceId,
43116
+ path: A2UI_SPEC_FORM_PATH,
43117
+ value: {
43118
+ ...defaults,
43119
+ ...previousValues
43120
+ }
43121
+ }
43122
+ }]);
43123
+ }
43032
43124
  } catch (error) {
43033
43125
  reportA2uiFallback(surfaceId, "spec", error);
43034
43126
  if (!disposed) setFallback(fallbackReason(error));
@@ -43036,6 +43128,8 @@ function A2uiSpecSurface({ spec, surfaceId, actions, onAction, registry, runId,
43036
43128
  })();
43037
43129
  return () => {
43038
43130
  disposed = true;
43131
+ const values = surfaceElement?.surface?.dataModel.get(A2UI_SPEC_FORM_PATH);
43132
+ if (typeof values === "object" && values !== null && !Array.isArray(values)) formValuesRef.current = values;
43039
43133
  surfaceElement?.remove();
43040
43134
  };
43041
43135
  }, [spec, surfaceId]);
@@ -46053,6 +46147,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
46053
46147
  const [loadingEarlierMessages, setLoadingEarlierMessages] = useState(false);
46054
46148
  const [currentSessionId, setCurrentSessionId] = useState();
46055
46149
  const [sending, setSending] = useState(false);
46150
+ const [processingAttachments, setProcessingAttachments] = useState(false);
46056
46151
  /** 这一条排在同会话的上一条后面(FR-20.3):排队本身保护历史顺序,要改的是它不可见 */
46057
46152
  const [queuedSend, setQueuedSend] = useState(false);
46058
46153
  /**
@@ -46323,6 +46418,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
46323
46418
  setError(void 0);
46324
46419
  break;
46325
46420
  case "message-complete":
46421
+ if (event.message.role === "user") setProcessingAttachments(false);
46326
46422
  setMessages((prev) => prev.some((message) => message.id === event.message.id) ? prev : [...prev, event.message]);
46327
46423
  break;
46328
46424
  case "interaction-requested":
@@ -46396,7 +46492,13 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
46396
46492
  setNoModelGuideOpen(true);
46397
46493
  return;
46398
46494
  }
46495
+ const hasAttachments = attachments.length > 0;
46496
+ if (hasAttachments) {
46497
+ setProcessingAttachments(true);
46498
+ setSending(true);
46499
+ }
46399
46500
  (async () => {
46501
+ if (hasAttachments) await new Promise((resolve) => setTimeout(resolve, 0));
46400
46502
  await flushPendingDeletes();
46401
46503
  let sessionId = currentSessionId;
46402
46504
  if (!sessionId) {
@@ -46408,7 +46510,13 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
46408
46510
  const needsTitle = sessionsRef.current.find((session) => session.id === sessionId)?.title === void 0;
46409
46511
  await engine.send(text, attachments);
46410
46512
  if (needsTitle) await refreshSessions();
46411
- })().catch((e) => setError({ message: toErrorMessage(e) }));
46513
+ })().catch((e) => {
46514
+ if (hasAttachments) {
46515
+ setProcessingAttachments(false);
46516
+ setSending(false);
46517
+ }
46518
+ setError({ message: toErrorMessage(e) });
46519
+ });
46412
46520
  }, [
46413
46521
  engine,
46414
46522
  currentSessionId,
@@ -46764,6 +46872,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
46764
46872
  } } : {},
46765
46873
  running: busy,
46766
46874
  disabled: interactionPending,
46875
+ processingAttachments,
46767
46876
  ...interactionTarget !== void 0 ? { waitTarget: interactionTarget } : {},
46768
46877
  centered: composerCentered,
46769
46878
  attachments: attachmentsReady,
@@ -46852,7 +46961,7 @@ function VercelPayloadPreview({ bridge }) {
46852
46961
  * Version of the published `@webskill/chatbot` package, injected at build time.
46853
46962
  * @stable
46854
46963
  */
46855
- const CHATBOT_VERSION = "0.24.3";
46964
+ const CHATBOT_VERSION = "0.25.1";
46856
46965
 
46857
46966
  //#endregion
46858
46967
  export { A2uiSurfaceHost, A2uiSurfaceSnapshotHost, CHATBOT_VERSION, ChatEngine, Chatbot, ChatbotVisionDelegate, CompositeUiBridge, DEFAULT_RENDERER_CAPABILITIES, DEFAULT_RUNTIME_CONFIG_STORAGE_KEY, ErrorCard, InteractionCard, InterruptedBanner, OpenUiSurfaceHost, OpenUiSurfaceSnapshotHost, ResultBlockList, ResultBlocksPro, SkillBadges, SpecInteraction, VercelPayloadPreview, VercelSurfaceHost, VercelSurfaceSnapshotHost, WEB_OFFICE_TEMPLATE_PREFIX, chatbotDictionary, configureA2uiMarkdown, createHttpMessageComponent, 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.24.3",
3
+ "version": "0.25.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -38,7 +38,7 @@
38
38
  "@a2ui/web_core": "^0.10.5",
39
39
  "@lit/context": "^1.1.0",
40
40
  "@openuidev/react-lang": "0.2.8",
41
- "@webskill/sdk": "^0.27.1",
41
+ "@webskill/sdk": "^0.28.1",
42
42
  "mermaid": "^12.0.0",
43
43
  "react": ">=19.0.0",
44
44
  "react-dom": ">=19.0.0",