@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/chatbot.css +5 -6
- package/dist/index.d.ts +630 -489
- package/dist/index.js +196 -94
- package/package.json +2 -2
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:
|
|
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
|
-
*
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
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"
|