@webskill/chatbot 0.24.2 → 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 +8 -0
- package/dist/config.d.ts +16 -3
- package/dist/config.js +129 -21
- package/dist/index.d.ts +6 -2
- package/dist/index.js +173 -32
- package/package.json +2 -2
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/config.d.ts
CHANGED
|
@@ -58,15 +58,22 @@ interface ApplyConfigDefaultsInput {
|
|
|
58
58
|
/** 上次应用过的缺省值快照,可缺席 */
|
|
59
59
|
readonly lastApplied?: WebSkillConfigDefaults;
|
|
60
60
|
}
|
|
61
|
-
/**
|
|
61
|
+
/** 合并结果:可直接使用的配置,以及要持久化的本次快照 @experimental */
|
|
62
62
|
interface AppliedConfigDefaults {
|
|
63
63
|
readonly config: RuntimeConfig;
|
|
64
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* 本次应用的快照;调用方负责持久化,下次作为 `lastApplied` 传回。
|
|
66
|
+
* 有烘焙模型时它还带一个内部同步状态(0.27.1),**必须整对象存取**——
|
|
67
|
+
* 只挑 `WebSkillConfigDefaults` 的已知键会把状态丢掉。
|
|
68
|
+
*/
|
|
65
69
|
readonly snapshot: WebSkillConfigDefaults;
|
|
66
70
|
}
|
|
67
71
|
/**
|
|
68
72
|
* `lastApplied` 缺席时每条分支都退化成「stored 存在就取 stored」,
|
|
69
73
|
* 即老实现的 `deepMerge(defaults, stored)`——修订跟随是纯增量,不改既有语义。
|
|
74
|
+
*
|
|
75
|
+
* 快照里的内部同步状态只在有烘焙模型时出现;它不是 `RuntimeConfig` 字段,
|
|
76
|
+
* 宿主照旧整存整取快照即可。
|
|
70
77
|
* @experimental
|
|
71
78
|
*/
|
|
72
79
|
declare function applyConfigDefaults(input: ApplyConfigDefaultsInput): AppliedConfigDefaults;
|
|
@@ -97,7 +104,13 @@ declare const BAKED_KEY_SENTINEL = "\0webskill:baked-api-key";
|
|
|
97
104
|
* @experimental
|
|
98
105
|
*/
|
|
99
106
|
type BakedSecretLoader = () => Promise<Readonly<Record<string, string>>>;
|
|
100
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* 上次生效的缺省值快照仓;存哪儿由宿主决定。
|
|
109
|
+
*
|
|
110
|
+
* 快照除了 `WebSkillConfigDefaults` 还可能带内部同步状态(0.27.1),
|
|
111
|
+
* 因此宿主必须**整对象存取**,不能只挑已知键。
|
|
112
|
+
* @experimental
|
|
113
|
+
*/
|
|
101
114
|
interface ConfigDefaultsSnapshotStore {
|
|
102
115
|
read(): Promise<WebSkillConfigDefaults | undefined> | WebSkillConfigDefaults | undefined;
|
|
103
116
|
write(snapshot: WebSkillConfigDefaults): Promise<void> | void;
|
package/dist/config.js
CHANGED
|
@@ -57,6 +57,7 @@ function isAllowedSite(url, allowedHosts) {
|
|
|
57
57
|
|
|
58
58
|
//#endregion
|
|
59
59
|
//#region src/config/merge.ts
|
|
60
|
+
const BAKED_SYNC_KEY = "__webskillBakedSync";
|
|
60
61
|
function isPlainObject(value) {
|
|
61
62
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
62
63
|
}
|
|
@@ -91,28 +92,125 @@ function mergeLeaf(stored, defaultValue, lastApplied) {
|
|
|
91
92
|
if (lastApplied !== void 0 && sameValue(stored, lastApplied)) return defaultValue;
|
|
92
93
|
return stored;
|
|
93
94
|
}
|
|
95
|
+
/** 模型列表内容的摘要;只用于判断「与上次记录相比有没有变」,不是加密承诺 */
|
|
96
|
+
function fingerprintEntries(value) {
|
|
97
|
+
let text;
|
|
98
|
+
try {
|
|
99
|
+
text = JSON.stringify(value ?? []) ?? "null";
|
|
100
|
+
} catch {
|
|
101
|
+
return "unserializable";
|
|
102
|
+
}
|
|
103
|
+
let h1 = 2166136261;
|
|
104
|
+
let h2 = 16777619;
|
|
105
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
106
|
+
const code = text.charCodeAt(i);
|
|
107
|
+
h1 = Math.imul(h1 ^ code, 16777619) >>> 0;
|
|
108
|
+
h2 = Math.imul(h2 ^ code, 2246822507) >>> 0;
|
|
109
|
+
}
|
|
110
|
+
return `${text.length}:${h1.toString(16)}:${h2.toString(16)}`;
|
|
111
|
+
}
|
|
112
|
+
/** 读快照里的内部状态;形状非法时当作没有(旧快照 / 手改过的存储) */
|
|
113
|
+
function readBakedSync(snapshot) {
|
|
114
|
+
if (snapshot === void 0) return void 0;
|
|
115
|
+
const value = snapshot[BAKED_SYNC_KEY];
|
|
116
|
+
if (!isPlainObject(value)) return void 0;
|
|
117
|
+
const mode = value["mode"];
|
|
118
|
+
const fingerprint = value["fingerprint"];
|
|
119
|
+
if ((mode === "baked" || mode === "repair" || mode === "user") && typeof fingerprint === "string") return {
|
|
120
|
+
mode,
|
|
121
|
+
fingerprint
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 旧快照的一次性修复:只补烘焙条目里存储缺失的键,不覆盖已有值。
|
|
126
|
+
*
|
|
127
|
+
* 旧快照分不出「上一次下发留下的旧条目」与「用户主动清空过一个字段」,
|
|
128
|
+
* 所以只能补缺失:`contextWindow`、`capabilities.image` 这类新字段能回来,
|
|
129
|
+
* 用户改过的 `baseUrl` / `apiKey` / `image: false` 一个字不动。
|
|
130
|
+
*/
|
|
131
|
+
function fillMissingValue(current, fallback) {
|
|
132
|
+
if (current === void 0) return fallback;
|
|
133
|
+
if (isPlainObject(current) && isPlainObject(fallback)) {
|
|
134
|
+
const out = { ...current };
|
|
135
|
+
for (const key of Object.keys(fallback)) {
|
|
136
|
+
if (key === "apiKey") continue;
|
|
137
|
+
const value = fillMissingValue(current[key], fallback[key]);
|
|
138
|
+
if (value !== void 0) out[key] = value;
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
return current;
|
|
143
|
+
}
|
|
144
|
+
function repairBakedEntries(entries, baked) {
|
|
145
|
+
const byId = new Map(baked.map((entry) => [entry.id, entry]));
|
|
146
|
+
return entries.map((entry) => {
|
|
147
|
+
const source = byId.get(entry.id);
|
|
148
|
+
return source === void 0 ? entry : fillMissingValue(entry, source);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
94
151
|
/**
|
|
95
|
-
* 烘焙模型按 id 强制同步(0.21.0 分册 24 · FR-24.1
|
|
152
|
+
* 烘焙模型按 id 强制同步(0.21.0 分册 24 · FR-24.1),并记住「下发/修复之后
|
|
153
|
+
* 用户存储有没有变」(0.27.1 分册 10)。
|
|
96
154
|
*
|
|
97
155
|
* 与 `mergeLeaf` 的「缺省值」语义不同,这里是**下发**:整片 `entries` 是一片叶子,
|
|
98
|
-
*
|
|
156
|
+
* 不做字段级合并,以免造出「一半新一半旧」的端点。
|
|
99
157
|
* 所以烘焙条目按 id 整条覆盖、按 defaults 的顺序在前,用户自建的按原相对序在末尾。
|
|
100
158
|
*
|
|
101
|
-
*
|
|
159
|
+
* 下发只在**运维真的重新打包过模型配置时**发生(FR-24.1d):烘焙列表与快照逐字
|
|
102
160
|
* 相同,说明这一版没人改过 `config.json`,此时用户在 Console 里的改动与排序全部作数。
|
|
103
161
|
* 快照缺席(全新安装或重装扩展)按「变了」处理——那正是「恢复初始化配置」的时刻。
|
|
104
162
|
*
|
|
105
163
|
* 读的是 `stored` 原始值而不是合并后的那份:合并后的 `entries` 已经被叶子规则
|
|
106
164
|
* 选过边,从它数不准「用户自建了哪些」。
|
|
165
|
+
*
|
|
166
|
+
* 0.27.1 之前,下发结果没有落进 `stored`,快照却在第一次加载后变成了新烘焙列表;
|
|
167
|
+
* 第二次加载因此误判「用户没动过」的分支为「用户胜出」,把刚下发的字段丢掉。
|
|
168
|
+
* 内部状态记录了下发那一刻 `stored` 的指纹:指纹没变就继续下发/修复,
|
|
169
|
+
* 变了才是用户真的动过。
|
|
107
170
|
*/
|
|
108
|
-
function syncBakedEntries(stored, baked, previous, retired) {
|
|
171
|
+
function syncBakedEntries(stored, baked, previous, lastApplied, retired) {
|
|
172
|
+
const raw = Array.isArray(stored) ? stored : [];
|
|
173
|
+
const fingerprint = fingerprintEntries(raw);
|
|
174
|
+
const state = readBakedSync(lastApplied);
|
|
175
|
+
const bakedChanged = previous === void 0 || !sameValue(previous, baked);
|
|
176
|
+
const kept = raw.filter((entry) => isPlainObject(entry) && typeof entry["id"] === "string" && !retired.has(entry["id"]));
|
|
109
177
|
const bakedIds = new Set(baked.map((entry) => entry.id));
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
178
|
+
const keptIds = new Set(kept.map((entry) => entry.id));
|
|
179
|
+
const bakedFirst = [...baked, ...kept.filter((entry) => !bakedIds.has(entry.id))];
|
|
180
|
+
const storedFirst = [...kept, ...baked.filter((entry) => !keptIds.has(entry.id))];
|
|
181
|
+
if (bakedChanged) return {
|
|
182
|
+
entries: bakedFirst,
|
|
183
|
+
state: {
|
|
184
|
+
mode: "baked",
|
|
185
|
+
fingerprint
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
if (state === void 0) return {
|
|
189
|
+
entries: repairBakedEntries(storedFirst, baked),
|
|
190
|
+
state: {
|
|
191
|
+
mode: "repair",
|
|
192
|
+
fingerprint
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
if (state.fingerprint !== fingerprint) return {
|
|
196
|
+
entries: storedFirst,
|
|
197
|
+
state: {
|
|
198
|
+
mode: "user",
|
|
199
|
+
fingerprint
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
if (state.mode === "baked") return {
|
|
203
|
+
entries: bakedFirst,
|
|
204
|
+
state
|
|
205
|
+
};
|
|
206
|
+
if (state.mode === "repair") return {
|
|
207
|
+
entries: repairBakedEntries(storedFirst, baked),
|
|
208
|
+
state
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
entries: storedFirst,
|
|
212
|
+
state
|
|
213
|
+
};
|
|
116
214
|
}
|
|
117
215
|
/**
|
|
118
216
|
* 上一版烘过、这一版不再烘的 id(FR-24.5)。
|
|
@@ -133,30 +231,40 @@ function resolveDefaultId(entries, current) {
|
|
|
133
231
|
/** `llm.entries` 之外的一切仍走 0.20.0 的逐叶子规则 */
|
|
134
232
|
function applyBakedModels(merged, stored, defaults, lastApplied) {
|
|
135
233
|
const baked = defaults.llm?.entries;
|
|
136
|
-
if (baked === void 0 || !isPlainObject(merged)) return merged;
|
|
234
|
+
if (baked === void 0 || !isPlainObject(merged)) return { merged };
|
|
137
235
|
const storedLlm = isPlainObject(stored) ? stored["llm"] : void 0;
|
|
138
|
-
const
|
|
236
|
+
const sync = syncBakedEntries(isPlainObject(storedLlm) ? storedLlm["entries"] : void 0, baked, lastApplied?.llm?.entries, lastApplied, retiredBakedIds(lastApplied, baked));
|
|
139
237
|
const llm = isPlainObject(merged["llm"]) ? merged["llm"] : {};
|
|
140
|
-
const defaultId = resolveDefaultId(entries, llm["defaultId"]);
|
|
238
|
+
const defaultId = resolveDefaultId(sync.entries, llm["defaultId"]);
|
|
141
239
|
return {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
240
|
+
merged: {
|
|
241
|
+
...merged,
|
|
242
|
+
llm: {
|
|
243
|
+
...llm,
|
|
244
|
+
entries: sync.entries,
|
|
245
|
+
...defaultId === void 0 ? {} : { defaultId }
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
state: sync.state
|
|
148
249
|
};
|
|
149
250
|
}
|
|
150
251
|
/**
|
|
151
252
|
* `lastApplied` 缺席时每条分支都退化成「stored 存在就取 stored」,
|
|
152
253
|
* 即老实现的 `deepMerge(defaults, stored)`——修订跟随是纯增量,不改既有语义。
|
|
254
|
+
*
|
|
255
|
+
* 快照里的内部同步状态只在有烘焙模型时出现;它不是 `RuntimeConfig` 字段,
|
|
256
|
+
* 宿主照旧整存整取快照即可。
|
|
153
257
|
* @experimental
|
|
154
258
|
*/
|
|
155
259
|
function applyConfigDefaults(input) {
|
|
156
260
|
const { stored, defaults, lastApplied } = input;
|
|
261
|
+
const baked = applyBakedModels(stored === void 0 ? defaults : mergeLeaf(stored, defaults, lastApplied), stored, defaults, lastApplied);
|
|
157
262
|
return {
|
|
158
|
-
config: mergeRuntimeConfigDefaults(
|
|
159
|
-
snapshot: defaults
|
|
263
|
+
config: mergeRuntimeConfigDefaults(baked.merged),
|
|
264
|
+
snapshot: baked.state === void 0 ? defaults : {
|
|
265
|
+
...defaults,
|
|
266
|
+
[BAKED_SYNC_KEY]: baked.state
|
|
267
|
+
}
|
|
160
268
|
};
|
|
161
269
|
}
|
|
162
270
|
|
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 →
|
|
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.
|
|
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
|
|
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
|
-
*
|
|
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 () =>
|
|
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
|
-
|
|
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:
|
|
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 () =>
|
|
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);
|
|
@@ -30180,6 +30219,45 @@ var ChatEngine = class {
|
|
|
30180
30219
|
*/
|
|
30181
30220
|
const hasTemplateEngine = Boolean(this.#adapter.xlsxTemplateEngine || this.#adapter.docxTemplateEngine);
|
|
30182
30221
|
const templateIdHints = [...this.#adapter.webOfficeTemplateSource ? [`For a document open in an online office viewer on the page, use "${WEB_OFFICE_TEMPLATE_PREFIX}<handle>" with the handle from list_weboffice_documents.`] : [], ...hasTemplateEngine && await this.#hasSkillResources(fs) ? [`For a file that ships inside an installed skill, use "${SKILL_FILE_PREFIX}<skill name>/<path inside the skill>", for example "${SKILL_FILE_PREFIX}quotation/assets/template.xlsx".`] : []];
|
|
30222
|
+
/**
|
|
30223
|
+
* 页面取像预算(0.27.1 分册 11)。
|
|
30224
|
+
*
|
|
30225
|
+
* 普通读页(`requested: false`)只认主模型自身能看图;显式 `images: true`
|
|
30226
|
+
* 才允许用可用的视觉委派兜底。两条路径共享同一份配置读取,避免数值漂移。
|
|
30227
|
+
*/
|
|
30228
|
+
const pageImageBudget = async (requested) => {
|
|
30229
|
+
const live = await this.#runtimeConfigStore()?.load();
|
|
30230
|
+
const maxImageBytes = live?.multimodal.maxImageBytes ?? 0;
|
|
30231
|
+
const maxImages = live?.multimodal.maxImagesPerMessage ?? 0;
|
|
30232
|
+
const minImageArea = live?.multimodal.minImageArea ?? 0;
|
|
30233
|
+
if (!(live?.multimodal.pageImageCapture ?? false)) return {
|
|
30234
|
+
enabled: false,
|
|
30235
|
+
maxImageBytes,
|
|
30236
|
+
maxImages,
|
|
30237
|
+
minImageArea,
|
|
30238
|
+
unavailableReason: "Page image capture is turned off in the host settings."
|
|
30239
|
+
};
|
|
30240
|
+
if (entryCapabilities(pickLlmEntry(live, this.#selectedModelId)).image) return {
|
|
30241
|
+
enabled: true,
|
|
30242
|
+
maxImageBytes,
|
|
30243
|
+
maxImages,
|
|
30244
|
+
minImageArea
|
|
30245
|
+
};
|
|
30246
|
+
if (!requested) return {
|
|
30247
|
+
enabled: false,
|
|
30248
|
+
maxImageBytes,
|
|
30249
|
+
maxImages,
|
|
30250
|
+
minImageArea
|
|
30251
|
+
};
|
|
30252
|
+
const delegated = await this.#visionDelegate?.applies() === true;
|
|
30253
|
+
return {
|
|
30254
|
+
enabled: delegated,
|
|
30255
|
+
maxImageBytes,
|
|
30256
|
+
maxImages,
|
|
30257
|
+
minImageArea,
|
|
30258
|
+
...delegated ? {} : { unavailableReason: "No image-capable model is configured. Select a multimodal model or add one in Console." }
|
|
30259
|
+
};
|
|
30260
|
+
};
|
|
30183
30261
|
const baseTools = [
|
|
30184
30262
|
...pageSkillSource ? [pageSkillSource] : [],
|
|
30185
30263
|
createSpreadsheetToolSource({
|
|
@@ -30210,15 +30288,8 @@ var ChatEngine = class {
|
|
|
30210
30288
|
...pagePerception ? [createPagePerceptionToolSource({
|
|
30211
30289
|
policy: pagePerception,
|
|
30212
30290
|
...this.#adapter.pagePerceptionPaging === void 0 ? {} : { paging: () => this.#adapter.pagePerceptionPaging },
|
|
30213
|
-
imageCapture:
|
|
30214
|
-
|
|
30215
|
-
return {
|
|
30216
|
-
enabled: (live?.multimodal.pageImageCapture ?? false) && entryCapabilities(pickLlmEntry(live, this.#selectedModelId)).image,
|
|
30217
|
-
maxImageBytes: live?.multimodal.maxImageBytes ?? 0,
|
|
30218
|
-
maxImages: live?.multimodal.maxImagesPerMessage ?? 0,
|
|
30219
|
-
minImageArea: live?.multimodal.minImageArea ?? 0
|
|
30220
|
-
};
|
|
30221
|
-
}
|
|
30291
|
+
imageCapture: () => pageImageBudget(false),
|
|
30292
|
+
imageCaptureForRequest: () => pageImageBudget(true)
|
|
30222
30293
|
})] : [],
|
|
30223
30294
|
...this.#adapter.pageActions ? [createPageActionToolSource({ policy: this.#adapter.pageActions })] : [],
|
|
30224
30295
|
...this.#adapter.pageImage ? [createPageImageToolSource({ capture: (input, context) => this.#capturePageImage(this.#adapter.pageImage, artifactStore, input, context?.runId) })] : [],
|
|
@@ -30234,7 +30305,16 @@ var ChatEngine = class {
|
|
|
30234
30305
|
}),
|
|
30235
30306
|
...this.#adapter.xlsxTableReader ? [createTableQueryToolSource({
|
|
30236
30307
|
tables: { load: (id, context) => this.#loadTable(id, context, downloadPolicy) },
|
|
30237
|
-
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
|
+
}
|
|
30238
30318
|
})] : [],
|
|
30239
30319
|
...this.#adapter.xlsxTemplateEngine || this.#adapter.docxTemplateEngine ? [createTemplateToolSource({
|
|
30240
30320
|
files: { read: readTemplateBytes },
|
|
@@ -32410,7 +32490,7 @@ function ModelMenuButton({ models }) {
|
|
|
32410
32490
|
}, option.id))
|
|
32411
32491
|
})] });
|
|
32412
32492
|
}
|
|
32413
|
-
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 }) {
|
|
32414
32494
|
const t = useT();
|
|
32415
32495
|
const locale = useLocale();
|
|
32416
32496
|
const dictation = useComposerDictation(dictationLang ?? DEFAULT_DICTATION_LANG[locale], onDictationPermissionDenied);
|
|
@@ -32427,11 +32507,20 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
|
|
|
32427
32507
|
const cameraReason = !attachments ? t("composer.camera.noStorage") : attachmentReason ?? (camera.availability.available ? void 0 : t(`composer.camera.${camera.availability.reason}`));
|
|
32428
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}`);
|
|
32429
32509
|
const plainChatHintId = useId();
|
|
32430
|
-
return /* @__PURE__ */
|
|
32510
|
+
return /* @__PURE__ */ jsxs(ComposerPrimitive.Root, {
|
|
32431
32511
|
"data-testid": "assistant-ui-composer",
|
|
32432
32512
|
"data-centered": centered,
|
|
32433
32513
|
className: "webskill-aui-composer shrink-0 px-4 py-4",
|
|
32434
|
-
children: /* @__PURE__ */ jsxs(
|
|
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, {
|
|
32435
32524
|
disabled: !attachments,
|
|
32436
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",
|
|
32437
32526
|
children: [
|
|
@@ -32657,7 +32746,7 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
|
|
|
32657
32746
|
onCancel: camera.cancel
|
|
32658
32747
|
})
|
|
32659
32748
|
]
|
|
32660
|
-
})
|
|
32749
|
+
})]
|
|
32661
32750
|
});
|
|
32662
32751
|
}
|
|
32663
32752
|
|
|
@@ -42948,6 +43037,22 @@ function fallbackReason(error) {
|
|
|
42948
43037
|
const code = error?.code;
|
|
42949
43038
|
return typeof code === "string" && code !== "" ? code : "a2ui-render-failed";
|
|
42950
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
|
+
}
|
|
42951
43056
|
/**
|
|
42952
43057
|
* `a2ui` 档:catalog 声明树经 BYOC catalog 的 Lit 元素渲染。
|
|
42953
43058
|
* 元素与协议里公布的 `catalog_definition` 派生自同一份 shape,因此这里
|
|
@@ -42958,6 +43063,7 @@ function fallbackReason(error) {
|
|
|
42958
43063
|
*/
|
|
42959
43064
|
function A2uiSpecSurface({ spec, surfaceId, actions, onAction, registry, runId, draft, onDraftChange }) {
|
|
42960
43065
|
const mountRef = useRef(null);
|
|
43066
|
+
const formValuesRef = useRef({});
|
|
42961
43067
|
const [fallback, setFallback] = useState();
|
|
42962
43068
|
const emit = useEffectEvent((actionId, value) => {
|
|
42963
43069
|
const declared = (actions ?? []).find((action) => action.id === actionId);
|
|
@@ -42987,8 +43093,11 @@ function A2uiSpecSurface({ spec, surfaceId, actions, onAction, registry, runId,
|
|
|
42987
43093
|
const processor = new MessageProcessor([catalog, lit.basicCatalog], (action) => {
|
|
42988
43094
|
const decoded = fromA2uiSpecAction(action);
|
|
42989
43095
|
if (!decoded) return;
|
|
42990
|
-
const values =
|
|
42991
|
-
|
|
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);
|
|
42992
43101
|
}, { version: "v0.9.1" });
|
|
42993
43102
|
processor.onSurfaceCreated((surface) => {
|
|
42994
43103
|
if (disposed) return;
|
|
@@ -42997,6 +43106,21 @@ function A2uiSpecSurface({ spec, surfaceId, actions, onAction, registry, runId,
|
|
|
42997
43106
|
mount.replaceChildren(surfaceElement);
|
|
42998
43107
|
});
|
|
42999
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
|
+
}
|
|
43000
43124
|
} catch (error) {
|
|
43001
43125
|
reportA2uiFallback(surfaceId, "spec", error);
|
|
43002
43126
|
if (!disposed) setFallback(fallbackReason(error));
|
|
@@ -43004,6 +43128,8 @@ function A2uiSpecSurface({ spec, surfaceId, actions, onAction, registry, runId,
|
|
|
43004
43128
|
})();
|
|
43005
43129
|
return () => {
|
|
43006
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;
|
|
43007
43133
|
surfaceElement?.remove();
|
|
43008
43134
|
};
|
|
43009
43135
|
}, [spec, surfaceId]);
|
|
@@ -46021,6 +46147,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
46021
46147
|
const [loadingEarlierMessages, setLoadingEarlierMessages] = useState(false);
|
|
46022
46148
|
const [currentSessionId, setCurrentSessionId] = useState();
|
|
46023
46149
|
const [sending, setSending] = useState(false);
|
|
46150
|
+
const [processingAttachments, setProcessingAttachments] = useState(false);
|
|
46024
46151
|
/** 这一条排在同会话的上一条后面(FR-20.3):排队本身保护历史顺序,要改的是它不可见 */
|
|
46025
46152
|
const [queuedSend, setQueuedSend] = useState(false);
|
|
46026
46153
|
/**
|
|
@@ -46291,6 +46418,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
46291
46418
|
setError(void 0);
|
|
46292
46419
|
break;
|
|
46293
46420
|
case "message-complete":
|
|
46421
|
+
if (event.message.role === "user") setProcessingAttachments(false);
|
|
46294
46422
|
setMessages((prev) => prev.some((message) => message.id === event.message.id) ? prev : [...prev, event.message]);
|
|
46295
46423
|
break;
|
|
46296
46424
|
case "interaction-requested":
|
|
@@ -46364,7 +46492,13 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
46364
46492
|
setNoModelGuideOpen(true);
|
|
46365
46493
|
return;
|
|
46366
46494
|
}
|
|
46495
|
+
const hasAttachments = attachments.length > 0;
|
|
46496
|
+
if (hasAttachments) {
|
|
46497
|
+
setProcessingAttachments(true);
|
|
46498
|
+
setSending(true);
|
|
46499
|
+
}
|
|
46367
46500
|
(async () => {
|
|
46501
|
+
if (hasAttachments) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
46368
46502
|
await flushPendingDeletes();
|
|
46369
46503
|
let sessionId = currentSessionId;
|
|
46370
46504
|
if (!sessionId) {
|
|
@@ -46376,7 +46510,13 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
46376
46510
|
const needsTitle = sessionsRef.current.find((session) => session.id === sessionId)?.title === void 0;
|
|
46377
46511
|
await engine.send(text, attachments);
|
|
46378
46512
|
if (needsTitle) await refreshSessions();
|
|
46379
|
-
})().catch((e) =>
|
|
46513
|
+
})().catch((e) => {
|
|
46514
|
+
if (hasAttachments) {
|
|
46515
|
+
setProcessingAttachments(false);
|
|
46516
|
+
setSending(false);
|
|
46517
|
+
}
|
|
46518
|
+
setError({ message: toErrorMessage(e) });
|
|
46519
|
+
});
|
|
46380
46520
|
}, [
|
|
46381
46521
|
engine,
|
|
46382
46522
|
currentSessionId,
|
|
@@ -46732,6 +46872,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
46732
46872
|
} } : {},
|
|
46733
46873
|
running: busy,
|
|
46734
46874
|
disabled: interactionPending,
|
|
46875
|
+
processingAttachments,
|
|
46735
46876
|
...interactionTarget !== void 0 ? { waitTarget: interactionTarget } : {},
|
|
46736
46877
|
centered: composerCentered,
|
|
46737
46878
|
attachments: attachmentsReady,
|
|
@@ -46820,7 +46961,7 @@ function VercelPayloadPreview({ bridge }) {
|
|
|
46820
46961
|
* Version of the published `@webskill/chatbot` package, injected at build time.
|
|
46821
46962
|
* @stable
|
|
46822
46963
|
*/
|
|
46823
|
-
const CHATBOT_VERSION = "0.
|
|
46964
|
+
const CHATBOT_VERSION = "0.25.1";
|
|
46824
46965
|
|
|
46825
46966
|
//#endregion
|
|
46826
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.
|
|
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.
|
|
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",
|