@webskill/chatbot 0.0.1 → 0.0.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chunhui Mo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -133,7 +133,7 @@ implementation (OPFS storage, skills, demo/model providers, console embedding).
133
133
  `CompositeUiBridge`, the i18n dictionary. Covered by semver from 0.1.0.
134
134
  - **`@experimental`** — A2UI/OpenUI/Vercel surface hosts and snapshot hosts,
135
135
  `VercelPayloadPreview`, `configureA2uiMarkdown`, the `probe*` availability
136
- helpers, `RendererKind` (renamed in 0.3.0), and the `RunSnapshot` format.
136
+ helpers, `RendererKind` (renamed in 0.4.0), and the `RunSnapshot` format.
137
137
  These may change between 0.0.x releases.
138
138
 
139
139
  ## License
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AgentLoopConfig, ApprovalScope, BridgeCapabilities, ExternalSkillProvider, ExternalToolSource, FileSystemProvider, InteractionRequest, InteractionResponse, LlmClient, NetworkPolicy, RenderBlock, RenderResultRequest, RunSnapshot, RunSnapshot as RunSnapshot$1, RuntimePhase, RuntimeRun, ScriptExecutor, TraceEvent, UiBridge, UiSurfaceActionRequest, UiSurfaceActionResponse, UiSurfaceDrafts, UiSurfaceEvent, UiSurfaceSnapshot } from "@webskill/sdk";
1
+ import { AgentLoopConfig, ApprovalScope, BridgeCapabilities, ExternalSkillProvider, ExternalToolSource, FileSystemProvider, InteractionRequest, InteractionResponse, LlmClient, NetworkPolicy, RenderBlock, RenderResultRequest, RunSnapshot, RunSnapshot as RunSnapshot$1, RuntimePhase, ScriptExecutor, UiBridge, UiSurfaceActionRequest, UiSurfaceActionResponse, UiSurfaceDrafts, UiSurfaceEvent, UiSurfaceSnapshot } from "@webskill/sdk";
2
2
  import { SandboxMode } from "@webskill/sdk/browser";
3
3
  import { ReactBridgeState, SurfaceRegistry, UiSurfaceActionEvent } from "@webskill/sdk/ui-react";
4
4
  import "@webskill/sdk/ui";
@@ -452,23 +452,6 @@ declare class ChatEngine {
452
452
  resume(runId: string): Promise<void>;
453
453
  }
454
454
  //#endregion
455
- //#region src/core/traceSink.d.ts
456
- /**
457
- * run trace 落盘格式:与 console 包的共享约定(两包零依赖,console 的
458
- * createFsTraceSource(fs, chatRoot) 读同一位置),字段名不得擅自变更。
459
- */
460
- interface RunTraceFile {
461
- runId: string;
462
- sessionId: string;
463
- startedAt: string;
464
- endedAt?: string;
465
- status: 'completed' | 'failed' | 'cancelled';
466
- activeSkills: string[];
467
- events: TraceEvent[];
468
- }
469
- /** 写 `<chatRoot>/traces/<runId>.json`;写失败降级 console.warn,不阻断对话 */
470
- declare function writeRunTrace(fs: FileSystemProvider, chatRoot: string, run: RuntimeRun): Promise<void>;
471
- //#endregion
472
455
  //#region src/i18n.d.ts
473
456
  /**
474
457
  * chatbot 双语字典:全部 UI 文案的单一来源(英文为主,zh 条目齐全)。
@@ -1229,4 +1212,4 @@ interface ErrorCardProps {
1229
1212
  /** 结构化错误卡:错误码徽章 + 描述 + 建议(engine error 事件 / 失败 run) */
1230
1213
  declare function ErrorCard({ code, message, onDismiss }: ErrorCardProps): import("react").JSX.Element;
1231
1214
  //#endregion
1232
- export { A2uiSurfaceHost, type A2uiSurfaceHostProps, A2uiSurfaceSnapshotHost, type A2uiSurfaceSnapshotHostProps, type AppearanceChange, type ChatAttachmentInput, type ChatAttachmentMeta, ChatEngine, type ChatEngineOptions, type ChatEvent, type ChatLayout, type ChatMessage, type ChatSessionMeta, type ChatTheme, type ChatToolCall, Chatbot, type ChatbotConfig, type ChatbotHostAdapter, type ChatbotProps, CompositeUiBridge, type CompositeUiBridgeDeps, type DownloadableFile, ErrorCard, type ErrorCardProps, InteractionCard, type InteractionCardProps, InterruptedBanner, type InterruptedBannerProps, type LlmConfig, type Locale, OpenUiChannel, OpenUiInteraction, type OpenUiInteractionProps, type OpenUiSession, OpenUiSurfaceHost, type OpenUiSurfaceHostProps, OpenUiSurfaceSnapshotHost, type OpenUiSurfaceSnapshotHostProps, type RendererKind, type ResolvedChatLayout, ResultBlockList, type ResultBlockListProps, ResultBlocksPro, type ResultBlocksProProps, type RunSnapshot, type RunTraceFile, type SandboxExecutorDeps, SettingsDrawer, type SettingsDrawerProps, SkillBadges, VercelPayloadPreview, type VercelPayloadPreviewProps, VercelSurfaceHost, type VercelSurfaceHostProps, VercelSurfaceSnapshotHost, type VercelSurfaceSnapshotHostProps, chatbotDictionary, configureA2uiMarkdown, probeA2uiAvailability, probeOpenUiAvailability, useT, writeRunTrace };
1215
+ export { A2uiSurfaceHost, type A2uiSurfaceHostProps, A2uiSurfaceSnapshotHost, type A2uiSurfaceSnapshotHostProps, type AppearanceChange, type ChatAttachmentInput, type ChatAttachmentMeta, ChatEngine, type ChatEngineOptions, type ChatEvent, type ChatLayout, type ChatMessage, type ChatSessionMeta, type ChatTheme, type ChatToolCall, Chatbot, type ChatbotConfig, type ChatbotHostAdapter, type ChatbotProps, CompositeUiBridge, type CompositeUiBridgeDeps, type DownloadableFile, ErrorCard, type ErrorCardProps, InteractionCard, type InteractionCardProps, InterruptedBanner, type InterruptedBannerProps, type LlmConfig, type Locale, OpenUiChannel, OpenUiInteraction, type OpenUiInteractionProps, type OpenUiSession, OpenUiSurfaceHost, type OpenUiSurfaceHostProps, OpenUiSurfaceSnapshotHost, type OpenUiSurfaceSnapshotHostProps, type RendererKind, type ResolvedChatLayout, ResultBlockList, type ResultBlockListProps, ResultBlocksPro, type ResultBlocksProProps, type RunSnapshot, type SandboxExecutorDeps, SettingsDrawer, type SettingsDrawerProps, SkillBadges, VercelPayloadPreview, type VercelPayloadPreviewProps, VercelSurfaceHost, type VercelSurfaceHostProps, VercelSurfaceSnapshotHost, type VercelSurfaceSnapshotHostProps, chatbotDictionary, configureA2uiMarkdown, probeA2uiAvailability, probeOpenUiAvailability, useT };
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ 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 { AnthropicClient, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FullDisclosureRouter, GoogleGenAiClient, OpenAiCompatibleClient, SkillDiscovery, WebSkillError, WebSkillRuntime, messageOf } from "@webskill/sdk";
9
+ import { AnthropicClient, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FullDisclosureRouter, GoogleGenAiClient, OpenAiCompatibleClient, SkillDiscovery, WebSkillError, WebSkillRuntime, messageOf, summarizeToolCalls } from "@webskill/sdk";
10
10
  import { BrowserWorkerScriptExecutor } from "@webskill/sdk/browser";
11
11
  import { JsonRenderSpecSurface, NativeSpecSurface, OpenUiSpecSurface, ReactBridgeState, UiSurfaceList, UiSurfaceSnapshotList } from "@webskill/sdk/ui-react";
12
12
  import { A2UI_SPEC_FORM_PATH, LitRendererBridge, OPENUI_SURFACE_ACTION, VERCEL_SURFACE_ACTION_DATA_PART_TYPE, collectValues, createUiCatalogToolSource, fromA2uiSpecAction, fromA2uiSurfaceAction, fromOpenUiAction, fromOpenUiSurfaceAction, fromVercelSurfaceAction, interactionToFormModel, isUiSpecSurface, loadOpenUiPeers, loadWebSkillLitCatalog, renderMiniChart, shapeInteractionValue, toA2uiSpecMessages, toA2uiSurfaceAction, toA2uiSurfaceMessages, toOpenUiLang, toOpenUiSurfaceLang, toVercelSurfaceDataPart, toVercelToolInvocation, uiCatalog } from "@webskill/sdk/ui";
@@ -41024,31 +41024,11 @@ var CompositeUiBridge = class {
41024
41024
  }
41025
41025
  };
41026
41026
 
41027
- //#endregion
41028
- //#region src/core/traceSink.ts
41029
- /** 写 `<chatRoot>/traces/<runId>.json`;写失败降级 console.warn,不阻断对话 */
41030
- async function writeRunTrace(fs, chatRoot, run) {
41031
- const trace = {
41032
- runId: run.id,
41033
- sessionId: run.sessionId,
41034
- startedAt: run.startedAt,
41035
- ...run.endedAt ? { endedAt: run.endedAt } : {},
41036
- status: run.status === "completed" || run.status === "cancelled" ? run.status : "failed",
41037
- activeSkills: run.activeSkillNames,
41038
- events: run.trace
41039
- };
41040
- try {
41041
- await fs.writeText(`${chatRoot}/traces/${run.id}.json`, JSON.stringify(trace, null, 2));
41042
- } catch (e) {
41043
- console.warn(`Failed to persist run trace "${run.id}": ${messageOf(e)}`);
41044
- }
41045
- }
41046
-
41047
41027
  //#endregion
41048
41028
  //#region src/core/chatEngine.ts
41049
41029
  /** 注入模型的单个附件正文上限(超出截断并标注) */
41050
41030
  const ATTACHMENT_TEXT_LIMIT = 32 * 1024;
41051
- /** 会话文件列表元数据(旧会话文件无新字段时照常读) */
41031
+ /** sdk 的 `SessionMeta` chatbot 的列表视图类型(跨层适配,非复刻) */
41052
41032
  const toSessionMeta = (session) => ({
41053
41033
  id: session.id,
41054
41034
  createdAt: session.createdAt,
@@ -41137,8 +41117,6 @@ var ChatEngine = class {
41137
41117
  #listeners = /* @__PURE__ */ new Set();
41138
41118
  /** runId → 已激活技能累积(skill-activated 事件用;终态清理) */
41139
41119
  #runSkills = /* @__PURE__ */ new Map();
41140
- /** runId → callId → 已发出的工具事件种类(live 与 trace 重放去重;同 callId 同事件不重复发) */
41141
- #emittedToolEvents = /* @__PURE__ */ new Map();
41142
41120
  /** 当前渲染框架档(复合 uiBridge 分发;setRenderer 热切换) */
41143
41121
  #renderer;
41144
41122
  /** a2ui 档交互容器(Chatbot ref 回调注入) */
@@ -41151,10 +41129,22 @@ var ChatEngine = class {
41151
41129
  #maxHistoryMessages;
41152
41130
  /** Serializes OPFS draft writes so a slower prior keystroke cannot overwrite a newer value. */
41153
41131
  #surfaceDraftWrites = /* @__PURE__ */ new Map();
41132
+ /** run trace 落盘(写失败降级 warn,不阻断对话) */
41133
+ #traceStore;
41134
+ /** 会话落盘:本包内引用 `FsSessionStore` 的唯一处 */
41135
+ #sessions;
41154
41136
  constructor(adapter, options = {}) {
41155
41137
  this.#adapter = adapter;
41156
41138
  this.#options = options;
41157
41139
  this.#chatRoot = (options.chatRoot ?? "/chat").replace(/\/+$/, "");
41140
+ this.#traceStore = new FsRunTraceStore({
41141
+ root: `${this.#chatRoot}/traces`,
41142
+ fs: adapter.storage
41143
+ });
41144
+ this.#sessions = new FsSessionStore({
41145
+ root: `${this.#chatRoot}/sessions`,
41146
+ fs: adapter.storage
41147
+ });
41158
41148
  this.#renderer = options.renderer ?? "native";
41159
41149
  this.bridge = new ReactBridgeState({ onSurfaceDraftChange: (runId, surfaceId, value) => this.#persistSurfaceDraft(runId, surfaceId, value) });
41160
41150
  const accumulate = this.bridge.onTextDelta.bind(this.bridge);
@@ -41218,28 +41208,10 @@ var ChatEngine = class {
41218
41208
  this.reloadConfig();
41219
41209
  }
41220
41210
  async listSessions() {
41221
- const dir = `${this.#chatRoot}/sessions`;
41222
- if (!await this.#adapter.storage.exists(dir)) return [];
41223
- const entries = await this.#adapter.storage.list(dir);
41224
- const metas = [];
41225
- for (const entry of entries) {
41226
- if (entry.type !== "file" || !entry.path.endsWith(".json")) continue;
41227
- try {
41228
- const session = JSON.parse(await this.#adapter.storage.readText(entry.path));
41229
- metas.push(toSessionMeta(session));
41230
- } catch (e) {
41231
- console.warn(`Skipping corrupt session file "${entry.path}": ${messageOf(e)}`);
41232
- }
41233
- }
41234
- return metas.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
41211
+ return (await this.#sessions.list({ includeArchived: true })).map(toSessionMeta);
41235
41212
  }
41236
41213
  async createSession() {
41237
- const session = {
41238
- id: newId("session"),
41239
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
41240
- messages: []
41241
- };
41242
- await this.#writeSession(session);
41214
+ const session = await this.#sessions.create({ id: newId("session") });
41243
41215
  this.#currentSessionId = session.id;
41244
41216
  return {
41245
41217
  id: session.id,
@@ -41247,13 +41219,12 @@ var ChatEngine = class {
41247
41219
  };
41248
41220
  }
41249
41221
  async selectSession(id) {
41250
- const session = await this.#readSession(id);
41222
+ const session = await this.#requireSession(id);
41251
41223
  this.#currentSessionId = id;
41252
41224
  return session.messages;
41253
41225
  }
41254
41226
  async deleteSession(id) {
41255
- const path = this.#sessionPath(id);
41256
- if (await this.#adapter.storage.exists(path)) await this.#adapter.storage.remove(path);
41227
+ await this.#sessions.delete(id);
41257
41228
  const attachments = `${this.attachmentsRoot}/${id}`;
41258
41229
  if (await this.#adapter.storage.exists(attachments)) await this.#adapter.storage.remove(attachments, { recursive: true });
41259
41230
  this.#handles.delete(id);
@@ -41261,36 +41232,32 @@ var ChatEngine = class {
41261
41232
  }
41262
41233
  /** 重命名会话:写入 title 并锁定(后续 send 不再用首条消息覆盖) */
41263
41234
  async renameSession(id, title) {
41264
- const session = await this.#readSession(id);
41265
- session.title = title;
41266
- session.titleLocked = true;
41267
- await this.#writeSession(session);
41235
+ await this.#sessions.setTitle(id, title, { lock: true });
41268
41236
  this.#emit({
41269
41237
  type: "session-updated",
41270
- session: toSessionMeta(session)
41238
+ session: toSessionMeta(await this.#requireSession(id))
41271
41239
  });
41272
41240
  }
41273
41241
  /** 归档会话(仅标记,消息与 trace 不动) */
41274
41242
  async archiveSession(id) {
41275
- await this.#setArchived(id, true);
41243
+ await this.#sessions.setArchived(id, true);
41244
+ this.#emit({
41245
+ type: "session-updated",
41246
+ session: toSessionMeta(await this.#requireSession(id))
41247
+ });
41276
41248
  }
41277
41249
  /** 取消归档 */
41278
41250
  async unarchiveSession(id) {
41279
- await this.#setArchived(id, false);
41280
- }
41281
- async #setArchived(id, archived) {
41282
- const session = await this.#readSession(id);
41283
- session.archived = archived;
41284
- await this.#writeSession(session);
41251
+ await this.#sessions.setArchived(id, false);
41285
41252
  this.#emit({
41286
41253
  type: "session-updated",
41287
- session: toSessionMeta(session)
41254
+ session: toSessionMeta(await this.#requireSession(id))
41288
41255
  });
41289
41256
  }
41290
41257
  /** 重发语义:取该 user 消息文本重新 send(assistant 应答新起一轮;不重写历史) */
41291
41258
  async retry(messageId) {
41292
41259
  const sessionId = this.#currentSessionId;
41293
- const message = (sessionId ? await this.#readSession(sessionId) : void 0)?.messages.find((m) => m.id === messageId);
41260
+ const message = (sessionId ? await this.#sessions.get(sessionId) : void 0)?.messages.find((m) => m.id === messageId);
41294
41261
  if (!sessionId || !message || message.role !== "user") throw new WebSkillError("VALIDATION_FAILED", `Cannot retry message "${messageId}": no matching user message in the active session`);
41295
41262
  await this.send(message.text);
41296
41263
  }
@@ -41300,23 +41267,21 @@ var ChatEngine = class {
41300
41267
  */
41301
41268
  async editAndResend(messageId, text) {
41302
41269
  const sessionId = this.#currentSessionId;
41303
- const session = sessionId ? await this.#readSession(sessionId) : void 0;
41270
+ const session = sessionId ? await this.#sessions.get(sessionId) : void 0;
41304
41271
  const index = session?.messages.findIndex((m) => m.id === messageId) ?? -1;
41305
41272
  const target = session && index >= 0 ? session.messages[index] : void 0;
41306
- if (!session || !target || target.role !== "user") throw new WebSkillError("VALIDATION_FAILED", `Cannot edit message "${messageId}": no matching user message in the active session`);
41307
- session.messages = session.messages.slice(0, index);
41308
- await this.#writeSession(session);
41273
+ if (!sessionId || !session || !target || target.role !== "user") throw new WebSkillError("VALIDATION_FAILED", `Cannot edit message "${messageId}": no matching user message in the active session`);
41274
+ await this.#sessions.replaceMessages(sessionId, session.messages.slice(0, index));
41309
41275
  await this.send(text);
41310
41276
  }
41311
41277
  /** 从当前会话消息列表删除指定消息并持久化(消息不存在/无当前会话时静默跳过) */
41312
41278
  async deleteMessage(messageId) {
41313
41279
  const sessionId = this.#currentSessionId;
41314
41280
  if (!sessionId) return;
41315
- const session = await this.#readSession(sessionId);
41281
+ const session = await this.#requireSession(sessionId);
41316
41282
  const next = session.messages.filter((m) => m.id !== messageId);
41317
41283
  if (next.length === session.messages.length) return;
41318
- session.messages = next;
41319
- await this.#writeSession(session);
41284
+ await this.#sessions.replaceMessages(sessionId, next);
41320
41285
  }
41321
41286
  /**
41322
41287
  * 驱动当前 session 的一轮 run。同 session 并发由 runtime session handle 内部
@@ -41327,7 +41292,7 @@ var ChatEngine = class {
41327
41292
  if (text.trim() === "" && attachments.length === 0) return;
41328
41293
  const runtime = await this.#ensureReady();
41329
41294
  const sessionId = this.#currentSessionId ?? (await this.createSession()).id;
41330
- const session = await this.#readSession(sessionId);
41295
+ const session = await this.#requireSession(sessionId);
41331
41296
  const prompt = await this.#withAttachments(text, attachments);
41332
41297
  const userMessage = {
41333
41298
  id: newId("msg"),
@@ -41339,9 +41304,8 @@ var ChatEngine = class {
41339
41304
  kind: "text"
41340
41305
  })) } : {}
41341
41306
  };
41342
- session.messages.push(userMessage);
41343
- if (!session.title && !session.titleLocked) session.title = text.slice(0, 30);
41344
- await this.#writeSession(session);
41307
+ await this.#sessions.appendMessages(sessionId, [userMessage]);
41308
+ if (!session.title && !session.titleLocked) await this.#sessions.setTitle(sessionId, text.slice(0, 30));
41345
41309
  this.#emit({
41346
41310
  type: "message-complete",
41347
41311
  message: userMessage
@@ -41349,7 +41313,7 @@ var ChatEngine = class {
41349
41313
  const handle = this.#sessionHandle(runtime, sessionId);
41350
41314
  try {
41351
41315
  const result = await handle.run(prompt);
41352
- await this.#finishRun(session, result);
41316
+ await this.#finishRun(sessionId, result);
41353
41317
  } catch (e) {
41354
41318
  this.#emitError(e);
41355
41319
  }
@@ -41393,8 +41357,9 @@ var ChatEngine = class {
41393
41357
  runId
41394
41358
  });
41395
41359
  const result = await runtime.resumeRun(runId);
41396
- const session = await this.#readSessionOrNew(result.run.sessionId);
41397
- await this.#finishRun(session, result);
41360
+ const sessionId = result.run.sessionId;
41361
+ if (!await this.#sessions.get(sessionId)) await this.#sessions.create({ id: sessionId });
41362
+ await this.#finishRun(sessionId, result);
41398
41363
  } catch (e) {
41399
41364
  this.#emitError(e);
41400
41365
  }
@@ -41557,33 +41522,30 @@ var ChatEngine = class {
41557
41522
  const callId = data["callId"];
41558
41523
  if (typeof name !== "string" || typeof callId !== "string") return;
41559
41524
  const args = typeof data["args"] === "string" ? { args: data["args"] } : {};
41560
- if (status === "started") {
41561
- if (this.#markToolEvent(event.runId, callId, "started")) this.#emit({
41562
- type: "tool-started",
41563
- runId: event.runId,
41564
- callId,
41565
- name,
41566
- ...args
41567
- });
41568
- } else if (status === "completed" || status === "failed") {
41569
- if (this.#markToolEvent(event.runId, callId, status)) this.#emit({
41570
- type: status === "completed" ? "tool-completed" : "tool-failed",
41571
- runId: event.runId,
41572
- callId,
41573
- name,
41574
- ...args
41575
- });
41576
- }
41525
+ if (status === "started") this.#emit({
41526
+ type: "tool-started",
41527
+ runId: event.runId,
41528
+ callId,
41529
+ name,
41530
+ ...args
41531
+ });
41532
+ else if (status === "completed" || status === "failed") this.#emit({
41533
+ type: status === "completed" ? "tool-completed" : "tool-failed",
41534
+ runId: event.runId,
41535
+ callId,
41536
+ name,
41537
+ ...args
41538
+ });
41577
41539
  } else if (event.phase === "complete" || event.phase === "fail") this.#runSkills.delete(event.runId);
41578
41540
  });
41579
41541
  this.#runtime = runtime;
41580
41542
  return runtime;
41581
41543
  }
41582
- /** run 终态公共路径(send / resume 共用):工具事件重放消息持久化 → trace 落盘 → 事件 */
41583
- async #finishRun(session, result) {
41544
+ /** run 终态公共路径(send / resume 共用):消息持久化 → trace 落盘 → 事件 */
41545
+ async #finishRun(sessionId, result) {
41584
41546
  const run = result.run;
41585
41547
  const status = run.status === "completed" || run.status === "cancelled" ? run.status : "failed";
41586
- const toolCalls = this.#replayToolEvents(run);
41548
+ const toolCalls = summarizeToolCalls(run);
41587
41549
  const latest = this.bridge.latestResult;
41588
41550
  const blocks = latest && latest.runId === run.id ? latest.blocks.filter((block) => block.type !== "markdown" || block.text.trim() !== result.output.trim()) : void 0;
41589
41551
  const surfaces = this.bridge.surfaceSnapshots.filter((snapshot) => snapshot.runId === run.id);
@@ -41598,9 +41560,8 @@ var ChatEngine = class {
41598
41560
  ...surfaces.length > 0 ? { surfaces } : {},
41599
41561
  ...toolCalls.length > 0 ? { toolCalls } : {}
41600
41562
  };
41601
- session.messages.push(assistantMessage);
41602
- await this.#writeSession(session);
41603
- await writeRunTrace(this.#adapter.storage, this.#chatRoot, run);
41563
+ await this.#sessions.appendMessages(sessionId, [assistantMessage]);
41564
+ await this.#traceStore.put(run);
41604
41565
  this.#emit({
41605
41566
  type: "run-completed",
41606
41567
  runId: run.id,
@@ -41613,67 +41574,6 @@ var ChatEngine = class {
41613
41574
  message: assistantMessage
41614
41575
  });
41615
41576
  this.#runSkills.delete(run.id);
41616
- this.#emittedToolEvents.delete(run.id);
41617
- }
41618
- /**
41619
- * 工具事件去重标记:live(runtime.events)与 trace 重放共用。
41620
- * 返回 true 表示该 (runId, callId, kind) 首次标记(应发出事件),false 表示已发过。
41621
- */
41622
- #markToolEvent(runId, callId, kind) {
41623
- let byCallId = this.#emittedToolEvents.get(runId);
41624
- if (!byCallId) {
41625
- byCallId = /* @__PURE__ */ new Map();
41626
- this.#emittedToolEvents.set(runId, byCallId);
41627
- }
41628
- let kinds = byCallId.get(callId);
41629
- if (!kinds) {
41630
- kinds = /* @__PURE__ */ new Set();
41631
- byCallId.set(callId, kinds);
41632
- }
41633
- if (kinds.has(kind)) return false;
41634
- kinds.add(kind);
41635
- return true;
41636
- }
41637
- /**
41638
- * 工具事件 trace 重放(去重兜底):live 已发出的 (callId, kind) 不重复发;
41639
- * 返回该 run 的工具调用终态序列(写入 assistant 消息,trace 为权威来源)。
41640
- */
41641
- #replayToolEvents(run) {
41642
- const toolCalls = [];
41643
- for (const event of run.trace) {
41644
- if (event.type !== "tool.started" && event.type !== "tool.completed" && event.type !== "tool.failed") continue;
41645
- const name = event.data?.["name"];
41646
- const callId = event.data?.["callId"];
41647
- if (typeof name !== "string" || typeof callId !== "string") continue;
41648
- const args = typeof event.data?.["args"] === "string" ? { args: event.data["args"] } : {};
41649
- if (event.type === "tool.started") {
41650
- if (this.#markToolEvent(run.id, callId, "started")) this.#emit({
41651
- type: "tool-started",
41652
- runId: run.id,
41653
- callId,
41654
- name,
41655
- ...args
41656
- });
41657
- } else {
41658
- const status = event.type === "tool.completed" ? "completed" : "failed";
41659
- const durationMs = typeof event.data?.["durationMs"] === "number" ? { durationMs: event.data["durationMs"] } : {};
41660
- if (this.#markToolEvent(run.id, callId, status)) this.#emit({
41661
- type: status === "completed" ? "tool-completed" : "tool-failed",
41662
- runId: run.id,
41663
- callId,
41664
- name,
41665
- ...args
41666
- });
41667
- toolCalls.push({
41668
- callId,
41669
- name,
41670
- status,
41671
- ...args,
41672
- ...durationMs
41673
- });
41674
- }
41675
- }
41676
- return toolCalls;
41677
41577
  }
41678
41578
  #snapshotStore() {
41679
41579
  return new FsRunSnapshotStore({
@@ -41693,26 +41593,10 @@ var ChatEngine = class {
41693
41593
  }
41694
41594
  return handle;
41695
41595
  }
41696
- #sessionPath(id) {
41697
- return `${this.#chatRoot}/sessions/${id}.json`;
41698
- }
41699
- async #readSession(id) {
41700
- const path = this.#sessionPath(id);
41701
- if (!await this.#adapter.storage.exists(path)) throw new WebSkillError("FS_NOT_FOUND", `Chat session "${id}" not found at ${path}`);
41702
- return JSON.parse(await this.#adapter.storage.readText(path));
41703
- }
41704
- /** resume 用:会话文件被删除时用空会话兜底(恢复不因此失败) */
41705
- async #readSessionOrNew(id) {
41706
- const path = this.#sessionPath(id);
41707
- if (await this.#adapter.storage.exists(path)) return JSON.parse(await this.#adapter.storage.readText(path));
41708
- return {
41709
- id,
41710
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
41711
- messages: []
41712
- };
41713
- }
41714
- async #writeSession(session) {
41715
- await this.#adapter.storage.writeText(this.#sessionPath(session.id), JSON.stringify(session, null, 2));
41596
+ async #requireSession(id) {
41597
+ const session = await this.#sessions.get(id);
41598
+ if (!session) throw new WebSkillError("FS_NOT_FOUND", `Chat session "${id}" not found under ${this.#chatRoot}/sessions`);
41599
+ return session;
41716
41600
  }
41717
41601
  };
41718
41602
 
@@ -55160,4 +55044,4 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
55160
55044
  }
55161
55045
 
55162
55046
  //#endregion
55163
- export { A2uiSurfaceHost, A2uiSurfaceSnapshotHost, ChatEngine, Chatbot, CompositeUiBridge, ErrorCard, InteractionCard, InterruptedBanner, OpenUiChannel, OpenUiInteraction, OpenUiSurfaceHost, OpenUiSurfaceSnapshotHost, ResultBlockList, ResultBlocksPro, SettingsDrawer, SkillBadges, VercelPayloadPreview, VercelSurfaceHost, VercelSurfaceSnapshotHost, chatbotDictionary, configureA2uiMarkdown, probeA2uiAvailability, probeOpenUiAvailability, useT, writeRunTrace };
55047
+ export { A2uiSurfaceHost, A2uiSurfaceSnapshotHost, ChatEngine, Chatbot, CompositeUiBridge, ErrorCard, InteractionCard, InterruptedBanner, OpenUiChannel, OpenUiInteraction, OpenUiSurfaceHost, OpenUiSurfaceSnapshotHost, ResultBlockList, ResultBlocksPro, SettingsDrawer, SkillBadges, VercelPayloadPreview, VercelSurfaceHost, VercelSurfaceSnapshotHost, chatbotDictionary, configureA2uiMarkdown, probeA2uiAvailability, probeOpenUiAvailability, useT };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@webskill/chatbot",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
+ "license": "MIT",
4
5
  "type": "module",
5
6
  "exports": {
6
7
  ".": {
@@ -25,7 +26,7 @@
25
26
  "@a2ui/web_core": "0.10.5",
26
27
  "@lit/context": "^1.1.0",
27
28
  "@openuidev/react-lang": "0.2.8",
28
- "@webskill/sdk": "^0.2.8",
29
+ "@webskill/sdk": "^0.3.0",
29
30
  "react": ">=19.0.0",
30
31
  "react-dom": ">=19.0.0",
31
32
  "zod": "^3.25.0 || ^4.0.0"