@webskill/chatbot 0.7.0 → 0.8.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 CHANGED
@@ -11,7 +11,6 @@
11
11
  --container-sm: 24rem;
12
12
  --container-md: 28rem;
13
13
  --container-2xl: 42rem;
14
- --container-3xl: 48rem;
15
14
  --text-xs: 0.75rem;
16
15
  --text-xs--line-height: calc(1 / 0.75);
17
16
  --text-sm: 0.875rem;
@@ -429,6 +428,9 @@
429
428
  .table {
430
429
  display: table;
431
430
  }
431
+ .aspect-\[4\/3\] {
432
+ aspect-ratio: 4/3;
433
+ }
432
434
  .aspect-square {
433
435
  aspect-ratio: 1 / 1;
434
436
  }
@@ -582,6 +584,9 @@
582
584
  .w-40 {
583
585
  width: calc(var(--spacing) * 40);
584
586
  }
587
+ .w-48 {
588
+ width: calc(var(--spacing) * 48);
589
+ }
585
590
  .w-72 {
586
591
  width: calc(var(--spacing) * 72);
587
592
  }
@@ -612,9 +617,6 @@
612
617
  .max-w-2xl {
613
618
  max-width: var(--container-2xl);
614
619
  }
615
- .max-w-3xl {
616
- max-width: var(--container-3xl);
617
- }
618
620
  .max-w-48 {
619
621
  max-width: calc(var(--spacing) * 48);
620
622
  }
@@ -725,6 +727,15 @@
725
727
  .resize-y {
726
728
  resize: vertical;
727
729
  }
730
+ .snap-x {
731
+ scroll-snap-type: x var(--tw-scroll-snap-strictness);
732
+ }
733
+ .snap-mandatory {
734
+ --tw-scroll-snap-strictness: mandatory;
735
+ }
736
+ .snap-start {
737
+ scroll-snap-align: start;
738
+ }
728
739
  .list-decimal {
729
740
  list-style-type: decimal;
730
741
  }
@@ -734,6 +745,9 @@
734
745
  .grid-cols-1 {
735
746
  grid-template-columns: repeat(1, minmax(0, 1fr));
736
747
  }
748
+ .grid-cols-\[auto_1fr\] {
749
+ grid-template-columns: auto 1fr;
750
+ }
737
751
  .flex-col {
738
752
  flex-direction: column;
739
753
  }
@@ -796,6 +810,12 @@
796
810
  margin-block-start: calc(var(--spacing) * var(--tw-space-y-reverse));
797
811
  margin-block-end: calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)));
798
812
  }
813
+ .gap-x-4 {
814
+ column-gap: calc(var(--spacing) * 4);
815
+ }
816
+ .gap-y-1 {
817
+ row-gap: var(--spacing);
818
+ }
799
819
  :where(.divide-y > :not(:last-child)) {
800
820
  --tw-divide-y-reverse: 0;
801
821
  border-bottom-style: var(--tw-border-style);
@@ -910,6 +930,10 @@
910
930
  border-left-style: var(--tw-border-style);
911
931
  border-left-width: 1px;
912
932
  }
933
+ .border-l-2 {
934
+ border-left-style: var(--tw-border-style);
935
+ border-left-width: 2px;
936
+ }
913
937
  .border-dashed {
914
938
  --tw-border-style: dashed;
915
939
  border-style: dashed;
@@ -998,18 +1022,6 @@
998
1022
  .bg-destructive-soft {
999
1023
  background-color: var(--destructive-soft);
1000
1024
  }
1001
- .bg-foreground\/35 {
1002
- background-color: var(--foreground);
1003
- @supports (color: color-mix(in lab, red, red)) {
1004
- background-color: color-mix(in oklab, var(--foreground) 35%, transparent);
1005
- }
1006
- }
1007
- .bg-foreground\/45 {
1008
- background-color: var(--foreground);
1009
- @supports (color: color-mix(in lab, red, red)) {
1010
- background-color: color-mix(in oklab, var(--foreground) 45%, transparent);
1011
- }
1012
- }
1013
1025
  .bg-info {
1014
1026
  background-color: var(--info);
1015
1027
  }
@@ -1019,12 +1031,6 @@
1019
1031
  .bg-ink {
1020
1032
  background-color: var(--foreground);
1021
1033
  }
1022
- .bg-ink\/35 {
1023
- background-color: var(--foreground);
1024
- @supports (color: color-mix(in lab, red, red)) {
1025
- background-color: color-mix(in oklab, var(--foreground) 35%, transparent);
1026
- }
1027
- }
1028
1034
  .bg-muted {
1029
1035
  background-color: var(--muted-foreground);
1030
1036
  }
@@ -1037,6 +1043,9 @@
1037
1043
  .bg-primary {
1038
1044
  background-color: var(--primary);
1039
1045
  }
1046
+ .bg-scrim {
1047
+ background-color: var(--scrim);
1048
+ }
1040
1049
  .bg-secondary {
1041
1050
  background-color: var(--secondary);
1042
1051
  }
@@ -1091,6 +1100,9 @@
1091
1100
  .p-4 {
1092
1101
  padding: calc(var(--spacing) * 4);
1093
1102
  }
1103
+ .p-5 {
1104
+ padding: calc(var(--spacing) * 5);
1105
+ }
1094
1106
  .px-1 {
1095
1107
  padding-inline: var(--spacing);
1096
1108
  }
@@ -1178,6 +1190,9 @@
1178
1190
  .pl-2 {
1179
1191
  padding-left: calc(var(--spacing) * 2);
1180
1192
  }
1193
+ .pl-3 {
1194
+ padding-left: calc(var(--spacing) * 3);
1195
+ }
1181
1196
  .pl-4 {
1182
1197
  padding-left: calc(var(--spacing) * 4);
1183
1198
  }
@@ -1323,6 +1338,9 @@
1323
1338
  .italic {
1324
1339
  font-style: italic;
1325
1340
  }
1341
+ .not-italic {
1342
+ font-style: normal;
1343
+ }
1326
1344
  .tabular-nums {
1327
1345
  --tw-numeric-spacing: tabular-nums;
1328
1346
  font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
@@ -2014,6 +2032,7 @@
2014
2032
  :where(.dark, .light, [data-theme]) {
2015
2033
  --color-background: var(--background);
2016
2034
  --color-foreground: var(--foreground);
2035
+ --color-scrim: var(--scrim);
2017
2036
  --color-card: var(--card);
2018
2037
  --color-card-foreground: var(--card-foreground);
2019
2038
  --color-popover: var(--popover);
@@ -2064,8 +2083,22 @@
2064
2083
  }
2065
2084
  :root, :where(.light, [data-theme='light']) {
2066
2085
  --radius: 0.625rem;
2086
+ --shadow-card-vivid: 0 6px 20px oklch(0 0 0 / 12%);
2087
+ --surface-dashboard: oklch(0.2 0.01 260);
2088
+ --border-dashboard: oklch(1 0 0 / 14%);
2089
+ --palette-sky: oklch(0.95 0.03 240);
2090
+ --palette-sky-ink: oklch(0.35 0.09 240);
2091
+ --palette-violet: oklch(0.95 0.03 300);
2092
+ --palette-violet-ink: oklch(0.35 0.1 300);
2093
+ --palette-amber: oklch(0.95 0.05 85);
2094
+ --palette-amber-ink: oklch(0.36 0.09 85);
2095
+ --palette-rose: oklch(0.95 0.03 15);
2096
+ --palette-rose-ink: oklch(0.37 0.11 15);
2097
+ --palette-emerald: oklch(0.95 0.04 160);
2098
+ --palette-emerald-ink: oklch(0.34 0.08 160);
2067
2099
  --background: oklch(1 0 0);
2068
2100
  --foreground: oklch(0.145 0 0);
2101
+ --scrim: oklch(0.145 0 0 / 45%);
2069
2102
  --card: oklch(1 0 0);
2070
2103
  --card-foreground: oklch(0.145 0 0);
2071
2104
  --popover: oklch(1 0 0);
@@ -2154,8 +2187,22 @@
2154
2187
  --code-url: hsl(198, 99%, 37%);
2155
2188
  }
2156
2189
  :where(.dark, [data-theme='dark']) {
2190
+ --shadow-card-vivid: 0 6px 20px oklch(0 0 0 / 40%);
2191
+ --surface-dashboard: oklch(0.18 0.01 260);
2192
+ --border-dashboard: oklch(1 0 0 / 16%);
2193
+ --palette-sky: oklch(0.3 0.05 240);
2194
+ --palette-sky-ink: oklch(0.88 0.06 240);
2195
+ --palette-violet: oklch(0.3 0.06 300);
2196
+ --palette-violet-ink: oklch(0.88 0.07 300);
2197
+ --palette-amber: oklch(0.32 0.06 85);
2198
+ --palette-amber-ink: oklch(0.9 0.09 85);
2199
+ --palette-rose: oklch(0.31 0.06 15);
2200
+ --palette-rose-ink: oklch(0.89 0.07 15);
2201
+ --palette-emerald: oklch(0.29 0.05 160);
2202
+ --palette-emerald-ink: oklch(0.88 0.08 160);
2157
2203
  --background: oklch(0.145 0 0);
2158
2204
  --foreground: oklch(0.985 0 0);
2205
+ --scrim: oklch(0 0 0 / 60%);
2159
2206
  --card: oklch(0.205 0 0);
2160
2207
  --card-foreground: oklch(0.985 0 0);
2161
2208
  --popover: oklch(0.205 0 0);
@@ -2531,8 +2578,86 @@
2531
2578
  .webskill-surface--error, .webskill-surface--unsupported {
2532
2579
  border-color: var(--destructive);
2533
2580
  }
2581
+ .webskill-palette-sky {
2582
+ background: var(--palette-sky);
2583
+ color: var(--palette-sky-ink);
2584
+ }
2585
+ .webskill-palette-violet {
2586
+ background: var(--palette-violet);
2587
+ color: var(--palette-violet-ink);
2588
+ }
2589
+ .webskill-palette-amber {
2590
+ background: var(--palette-amber);
2591
+ color: var(--palette-amber-ink);
2592
+ }
2593
+ .webskill-palette-rose {
2594
+ background: var(--palette-rose);
2595
+ color: var(--palette-rose-ink);
2596
+ }
2597
+ .webskill-palette-emerald {
2598
+ background: var(--palette-emerald);
2599
+ color: var(--palette-emerald-ink);
2600
+ }
2601
+ [data-webskill-theme='vivid'] {
2602
+ --radius-card: 1rem;
2603
+ --shadow-card: var(--shadow-card-vivid);
2604
+ }
2605
+ [data-webskill-theme='dashboard-dark'] {
2606
+ --radius-card: 0.375rem;
2607
+ --shadow-card: none;
2608
+ --color-surface: var(--surface-dashboard);
2609
+ --color-border: var(--border-dashboard);
2610
+ }
2611
+ .webskill-spec-split {
2612
+ container-type: inline-size;
2613
+ grid-template-columns: var(--split-ratio, 1fr 1fr);
2614
+ }
2615
+ .webskill-spec-card--span2 {
2616
+ grid-column: span 2;
2617
+ }
2618
+ @container (max-width: 30rem) {
2619
+ .webskill-spec-split {
2620
+ grid-template-columns: 1fr;
2621
+ }
2622
+ .webskill-spec-card--span2 {
2623
+ grid-column: auto;
2624
+ }
2625
+ }
2626
+ .webskill-spec-form__fields {
2627
+ display: grid;
2628
+ gap: 0.75rem;
2629
+ container-type: inline-size;
2630
+ grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
2631
+ }
2632
+ .webskill-spec-form__fields > :has(textarea), .webskill-spec-form__fields > [data-field-span='full'] {
2633
+ grid-column: 1 / -1;
2634
+ }
2534
2635
  .webskill-aui-shell {
2535
2636
  background: var(--color-background);
2637
+ --webskill-chat-prose-max: 48rem;
2638
+ --webskill-chat-surface-max: 64rem;
2639
+ }
2640
+ .webskill-aui-shell[data-layout='fullscreen'] {
2641
+ --webskill-chat-surface-max: 80rem;
2642
+ }
2643
+ .webskill-aui-shell[data-layout='mobile'] {
2644
+ --webskill-chat-prose-max: 100%;
2645
+ --webskill-chat-surface-max: 100%;
2646
+ }
2647
+ .webskill-chat-prose {
2648
+ max-width: var(--webskill-chat-prose-max);
2649
+ }
2650
+ .webskill-chat-column {
2651
+ container-type: inline-size;
2652
+ }
2653
+ .webskill-chat-surface-wide:has([data-webskill-multicol]) {
2654
+ --webskill-chat-surface-w: min(var(--webskill-chat-surface-max), 100cqw - 2rem);
2655
+ width: var(--webskill-chat-surface-w);
2656
+ margin-inline: calc((100% - var(--webskill-chat-surface-w)) / 2);
2657
+ }
2658
+ .webskill-aui-shell[data-layout='mobile'] .webskill-chat-surface-wide {
2659
+ width: 100%;
2660
+ margin-inline: 0;
2536
2661
  }
2537
2662
  .webskill-aui-header {
2538
2663
  border-color: var(--color-border);
@@ -2649,6 +2774,11 @@
2649
2774
  inherits: false;
2650
2775
  initial-value: 0;
2651
2776
  }
2777
+ @property --tw-scroll-snap-strictness {
2778
+ syntax: "*";
2779
+ inherits: false;
2780
+ initial-value: proximity;
2781
+ }
2652
2782
  @property --tw-space-y-reverse {
2653
2783
  syntax: "*";
2654
2784
  inherits: false;
@@ -2884,6 +3014,7 @@
2884
3014
  --tw-translate-x: 0;
2885
3015
  --tw-translate-y: 0;
2886
3016
  --tw-translate-z: 0;
3017
+ --tw-scroll-snap-strictness: proximity;
2887
3018
  --tw-space-y-reverse: 0;
2888
3019
  --tw-divide-y-reverse: 0;
2889
3020
  --tw-border-style: solid;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AgentLoopConfig, ApprovalScope, BridgeCapabilities, ExternalSkillProvider, ExternalToolSource, FileSystemProvider, HookRunner, InteractionRequest, InteractionResponse, LifecycleEventInit, LlmClient, NetworkPolicy, Page, PageQuery, RenderBlock, RenderResultRequest, RunSnapshot, RunSnapshotListEntry, RuntimePhase, RuntimeRun, ScriptExecutor, SessionStore, SkillCandidateMarker, SkillCatalogEntry, SkillIntegrityGuard, SkillOutcomeReporter, SkillStateGuard, UiBridge, UiSpecDrafts, UiSpecEvent, UiSpecSnapshot, UiSurfaceActionRequest, UiSurfaceActionResponse, UserProfile, UserProfileExport, WebSkillErrorCode, diffUserProfile } from "@webskill/sdk";
1
+ import { AgentLoopConfig, ApprovalScope, BridgeCapabilities, DocxTextExtractor, ExternalSkillProvider, ExternalToolSource, FileSystemProvider, HookRunner, InteractionRequest, InteractionResponse, LifecycleEventInit, LinkedDocumentReader, LlmClient, NetworkPolicy, Page, PageQuery, RenderBlock, RenderResultRequest, RunSnapshot, RunSnapshotListEntry, RuntimePhase, RuntimeRun, ScriptExecutor, SessionStore, SkillCandidateMarker, SkillCatalogEntry, SkillIntegrityGuard, SkillOutcomeReporter, SkillStateGuard, UiBridge, UiSpecDrafts, UiSpecEvent, UiSpecSnapshot, UiSurfaceActionRequest, UiSurfaceActionResponse, UserProfile, UserProfileExport, WebSkillErrorCode, diffUserProfile } from "@webskill/sdk";
2
2
  import { SandboxMode, TypeScriptSupportOptions } from "@webskill/sdk/browser";
3
3
  import { CustomSurfaceActionEvent, ReactBridgeState, SpecInteractionChannel, SurfaceRegistry, UiSurfaceActionEvent } from "@webskill/sdk/ui-react";
4
4
  import { PageActionPolicy, PagePerceptionPolicy, PerceptionRecord, SkillCandidateSink, TodoItem } from "@webskill/sdk/agent";
@@ -53,6 +53,10 @@ interface RuntimeLoopConfig {
53
53
  temperature?: number;
54
54
  toolResultMaxBytes: number;
55
55
  maxHistoryMessages: number;
56
+ /** `file` 分片的 base64 上限;超出即拒绝并说明,不截断(FR-23.4) */
57
+ maxDocumentBytes: number;
58
+ /** `document-text` 分片的字节上限;同样拒绝而不截断(FR-23.4) */
59
+ maxDocumentTextBytes: number;
56
60
  }
57
61
  interface RuntimeInteractionConfig {
58
62
  missingParams: 'user' | 'llm';
@@ -74,9 +78,22 @@ interface RuntimeSandboxConfig {
74
78
  networkPolicy: NetworkPolicy$1;
75
79
  capabilities: {
76
80
  readReference: CapabilityMode;
81
+ /** 同时管 readAsset 与 readAssetBinary */
82
+ readAsset: CapabilityMode;
77
83
  writeArtifact: CapabilityMode;
78
84
  confirm: CapabilityMode;
85
+ /**
86
+ * 脚本按宿主声明的数据源取数(分册 16)。
87
+ * 与 `readAsset` **相反**,缺省 `false`:它会出网,缺省开等于每个技能默认拿到出站能力。
88
+ */
89
+ fetchData: CapabilityMode;
79
90
  };
91
+ /**
92
+ * 单次取数结果的字节上限(分册 16)。超出即拒绝,**不截断**
93
+ * ——截断后的 JSON 解不出来,比拒绝更糟。结果回到脚本,不进模型上下文,
94
+ * 所以不受 `toolResultMaxBytes` 约束。
95
+ */
96
+ maxDataSourceBytes: number;
80
97
  /**
81
98
  * 出站 URL 准入策略(`RemoteUrlPolicy` 的可配置部分):技能安装、MCP 端点连接
82
99
  * 这类由宿主发起的取数都按它判定。两项都默认关,即 https-only 且拒绝内网/环回。
@@ -189,6 +206,17 @@ interface RuntimeAppearanceConfig {
189
206
  * 两者都会把图像随请求外发给模型提供方,属新增攻击面。
190
207
  * @experimental
191
208
  */
209
+ /**
210
+ * 文档投放面(0.11.0 分册 18)。
211
+ *
212
+ * **只有一个闸门字段**。CSP 白名单不在这里:它由 viewer 路由的**响应头**
213
+ * 下发(FR-18.3 第 1 条),是宿主部署时调 `viewerCspHeader` 的入参;
214
+ * 浏览器侧的运行时配置改不动已经发出去的响应头,携在这里只会是个不生效的假开关。
215
+ */
216
+ interface RuntimeDocumentSurfaceConfig {
217
+ /** 关着即没有这个投放面;开启还要求宿主额外提供一条 viewer 路由 */
218
+ enabled: boolean;
219
+ }
192
220
  interface RuntimeMultimodalConfig {
193
221
  imageAttachments: boolean;
194
222
  /** 页面图像抓取总开关(FR-12.3);关时感知只走纯文本路径 */
@@ -239,6 +267,7 @@ interface RuntimeConfig {
239
267
  security: RuntimeSecurityConfig;
240
268
  appearance: RuntimeAppearanceConfig;
241
269
  multimodal: RuntimeMultimodalConfig;
270
+ documentSurface: RuntimeDocumentSurfaceConfig;
242
271
  userProfile: RuntimeUserProfileConfig;
243
272
  skillState: RuntimeSkillStateConfig;
244
273
  /** 被用户删掉的自动模型条目 id(FR-14.3):删一次就不该再自己长回来 */
@@ -289,7 +318,7 @@ declare function createMemoryRuntimeConfigStore(initial?: Partial<RuntimeConfig>
289
318
  //#endregion
290
319
  //#region src/core/attachmentKind.d.ts
291
320
  /** 附件在模型侧的内容形态(0.6.0 FR-11.2) @experimental */
292
- type ChatAttachmentKind = 'text' | 'image' | 'file';
321
+ type ChatAttachmentKind = 'text' | 'image' | 'file' | 'document-text';
293
322
  //#endregion
294
323
  //#region src/core/types.d.ts
295
324
  /**
@@ -387,6 +416,8 @@ interface ChatRunPhase {
387
416
  endedAt?: number;
388
417
  /** 由结构化 `LifecycleEvent.data` 派生的一句摘要 */
389
418
  note?: string;
419
+ /** fail 相位的具体说明:`reason` 只有七种取值,说不出「哪个上限、哪个模型」 */
420
+ detail?: string;
390
421
  }
391
422
  /** 本轮发生的一次已提交交互(FR-17.8:提交后表单保留所填值) @experimental */
392
423
  interface ChatInteractionRecord {
@@ -630,7 +661,8 @@ interface ChatbotConfig {
630
661
  quickPrompts?: string[];
631
662
  /**
632
663
  * 开启后模型多一个 `render_ui` 工具,可面向 AI Component Catalog 直接生成声明式 UI。
633
- * 默认关:工具描述 + 入参 schema 约 14 KB,会计入每一次请求。
664
+ * 默认关:工具描述 + 入参 schema 约 34 KB,会计入每一次请求。
665
+ * 数字的单一来源是 `@webskill/ui` 的 `catalog/budget.ts`(实测终态 12 744 + 21 470)。
634
666
  *
635
667
  * 接了 `runtimeConfig` 时以 `RuntimeConfig.agentCapabilities.generativeUi` 为准
636
668
  * (设置抽屉里的开关就是它),本字段只在未接配置存储的宿主上生效。
@@ -649,6 +681,14 @@ interface ChatbotConfig {
649
681
  * `SKILL_GENERATION_DISABLED`,不弹确认框、不产生候选。
650
682
  */
651
683
  skillCandidates?: SkillCandidateSink;
684
+ /**
685
+ * 宿主声明的数据源取数入口(分册 16):透传给 `ChatEngineOptions.fetchData`。
686
+ * 不注入即脚本上下文没有 `fetchData` 这个键。
687
+ *
688
+ * **必须是稳定引用**(模块级或 `useMemo`):它进 `ChatEngine` 的 `useMemo`
689
+ * 依赖表,每次渲染新建一个箭头函数会把整个引擎重建掉。
690
+ */
691
+ fetchData?: (sourceId: string, params?: Record<string, unknown>) => Promise<unknown>;
652
692
  }
653
693
  //#endregion
654
694
  //#region src/core/hostAdapter.d.ts
@@ -685,9 +725,33 @@ interface ChatbotHostAdapter {
685
725
  * @experimental
686
726
  */
687
727
  pageActions?: PageActionPolicy;
728
+ /**
729
+ * 读页面链接指向的文档(分册 22)。实现住在 `@webskill/browser`,
730
+ * 而 chatbot 不依赖它 —— 与 `pagePerception` 同一条装配规矩:宿主注入。
731
+ * 不注入即 `read_linked_document` 不注册。
732
+ */
733
+ linkedDocuments?: LinkedDocumentReader;
734
+ /** docx → 文本(`extractDocxText`);不注入即 Word 文档明确报「本环境不支持」 */
735
+ docxExtractor?: DocxTextExtractor;
736
+ /** 文档读取留痕出口(FR-22.6) */
737
+ documentAudit?: {
738
+ append(event: {
739
+ type: string;
740
+ target: string;
741
+ data?: Record<string, unknown>;
742
+ }): Promise<unknown>;
743
+ };
688
744
  }
689
745
  //#endregion
690
746
  //#region src/core/chatEngine.d.ts
747
+ /**
748
+ * 条目能否真的发请求:云端要有端点或密钥,浏览器内置两者都不需要。
749
+ *
750
+ * 导出给 UI:选择器不用同一份判据的话,用户能选中一个空配置条目,
751
+ * 而引擎静默回退到另一条——界面说在用 A、实际跑的是 B(UI-UX8 D1)。
752
+ * @experimental
753
+ */
754
+ declare const isLlmEntryUsable: (entry: RuntimeLlmEntry) => boolean;
691
755
  /**
692
756
  * 选中条目 → 默认条目 → 首条;命中不可用条目时**回退到下一个可用条目**
693
757
  * (0.10.0 复核:选中项失效时宁可静默换用可用模型,也不能把 UI 锁死成
@@ -742,6 +806,14 @@ interface ChatEngineOptions {
742
806
  generativeUi?: boolean;
743
807
  /** 沙箱执行器替换点(测试注入 spy 断言构造参数;缺省 BrowserWorkerScriptExecutor) */
744
808
  executorFactory?: (deps: SandboxExecutorDeps) => ScriptExecutor;
809
+ /**
810
+ * 宿主声明的数据源取数入口(分册 16)。不注入即脚本上下文没有 `fetchData` 这个键。
811
+ *
812
+ * 装配点必须在宿主:策略本体 `DataSourcePolicy` 住在 `@webskill/agent`,
813
+ * 而数据源的目标地址是宿主自己的业务知识,chatbot 无从代为声明。
814
+ * 能力开关 `sandbox.capabilities.fetchData` 缺省关,注入了也还要宿主在配置里打开。
815
+ */
816
+ fetchData?: (sourceId: string, params?: Record<string, unknown>) => Promise<unknown>;
745
817
  /**
746
818
  * 会话落盘替换点(缺省 `FsSessionStore`,落在 `<chatRoot>/sessions`)。
747
819
  * 宕主换自己的实现(如 IndexedDB)与测试统计端口读写量都走这里。
@@ -1169,8 +1241,10 @@ declare const chatbotDictionary: {
1169
1241
  'composer.attachment.kind.text': string;
1170
1242
  'composer.attachment.kind.image': string;
1171
1243
  'composer.attachment.kind.file': string;
1244
+ 'composer.attachment.kind.document-text': string;
1172
1245
  'composer.attachment.compressed': string;
1173
1246
  'composer.model': string;
1247
+ 'composer.model.unconfigured': string;
1174
1248
  'composer.model.demo': string;
1175
1249
  'composer.noModel.title': string;
1176
1250
  'composer.noModel.description': string;
@@ -1506,8 +1580,10 @@ declare const chatbotDictionary: {
1506
1580
  'composer.attachment.kind.text': string;
1507
1581
  'composer.attachment.kind.image': string;
1508
1582
  'composer.attachment.kind.file': string;
1583
+ 'composer.attachment.kind.document-text': string;
1509
1584
  'composer.attachment.compressed': string;
1510
1585
  'composer.model': string;
1586
+ 'composer.model.unconfigured': string;
1511
1587
  'composer.model.demo': string;
1512
1588
  'composer.noModel.title': string;
1513
1589
  'composer.noModel.description': string;
@@ -1846,8 +1922,10 @@ declare const useT: () => TranslateFn<{
1846
1922
  'composer.attachment.kind.text': string;
1847
1923
  'composer.attachment.kind.image': string;
1848
1924
  'composer.attachment.kind.file': string;
1925
+ 'composer.attachment.kind.document-text': string;
1849
1926
  'composer.attachment.compressed': string;
1850
1927
  'composer.model': string;
1928
+ 'composer.model.unconfigured': string;
1851
1929
  'composer.model.demo': string;
1852
1930
  'composer.noModel.title': string;
1853
1931
  'composer.noModel.description': string;
@@ -2183,8 +2261,10 @@ declare const useT: () => TranslateFn<{
2183
2261
  'composer.attachment.kind.text': string;
2184
2262
  'composer.attachment.kind.image': string;
2185
2263
  'composer.attachment.kind.file': string;
2264
+ 'composer.attachment.kind.document-text': string;
2186
2265
  'composer.attachment.compressed': string;
2187
2266
  'composer.model': string;
2267
+ 'composer.model.unconfigured': string;
2188
2268
  'composer.model.demo': string;
2189
2269
  'composer.noModel.title': string;
2190
2270
  'composer.noModel.description': string;
@@ -2598,6 +2678,6 @@ declare function ErrorCard({ code, message, loopLimits, onOpenSettings, onDismis
2598
2678
  * Version of the published `@webskill/chatbot` package, injected at build time.
2599
2679
  * @stable
2600
2680
  */
2601
- declare const CHATBOT_VERSION = "0.7.0";
2681
+ declare const CHATBOT_VERSION = "0.8.0";
2602
2682
  //#endregion
2603
- export { A2uiSurfaceHost, type A2uiSurfaceHostProps, A2uiSurfaceSnapshotHost, type A2uiSurfaceSnapshotHostProps, type AppearanceChange, CHATBOT_VERSION, type ChatAttachmentInput, type ChatAttachmentMeta, 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, CompositeUiBridge, type CompositeUiBridgeDeps, DEFAULT_RENDERER_CAPABILITIES, DEFAULT_RUNTIME_CONFIG_STORAGE_KEY, type DownloadableFile, ErrorCard, type ErrorCardProps, InteractionCard, type InteractionCardProps, InterruptedBanner, type InterruptedBannerProps, type Locale, OpenUiSurfaceHost, type OpenUiSurfaceHostProps, OpenUiSurfaceSnapshotHost, type OpenUiSurfaceSnapshotHostProps, type RendererCapability, type RendererKind, type ResolvedChatLayout, ResultBlockList, type ResultBlockListProps, ResultBlocksPro, type ResultBlocksProProps, type RunSnapshot, type RuntimeAppearanceConfig, type RuntimeConfig, type RuntimeConfigStore, type RuntimeRendererId, type SandboxExecutorDeps, type SettingsSectionId, SkillBadges, SpecInteraction, type SpecInteractionProps, VercelPayloadPreview, type VercelPayloadPreviewProps, VercelSurfaceHost, type VercelSurfaceHostProps, VercelSurfaceSnapshotHost, type VercelSurfaceSnapshotHostProps, chatbotDictionary, configureA2uiMarkdown, createLocalStorageRuntimeConfigStore, createMemoryRuntimeConfigStore, pickUsableLlmEntry, probeA2uiAvailability, probeOpenUiAvailability, sandboxExecutorDeps, useT };
2683
+ export { A2uiSurfaceHost, type A2uiSurfaceHostProps, A2uiSurfaceSnapshotHost, type A2uiSurfaceSnapshotHostProps, type AppearanceChange, CHATBOT_VERSION, type ChatAttachmentInput, type ChatAttachmentMeta, 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, CompositeUiBridge, type CompositeUiBridgeDeps, DEFAULT_RENDERER_CAPABILITIES, DEFAULT_RUNTIME_CONFIG_STORAGE_KEY, type DownloadableFile, ErrorCard, type ErrorCardProps, InteractionCard, type InteractionCardProps, InterruptedBanner, type InterruptedBannerProps, type Locale, OpenUiSurfaceHost, type OpenUiSurfaceHostProps, OpenUiSurfaceSnapshotHost, type OpenUiSurfaceSnapshotHostProps, type RendererCapability, type RendererKind, type ResolvedChatLayout, ResultBlockList, type ResultBlockListProps, ResultBlocksPro, type ResultBlocksProProps, type RunSnapshot, type RuntimeAppearanceConfig, type RuntimeConfig, type RuntimeConfigStore, type RuntimeRendererId, type SandboxExecutorDeps, type SettingsSectionId, SkillBadges, SpecInteraction, type SpecInteractionProps, VercelPayloadPreview, type VercelPayloadPreviewProps, VercelSurfaceHost, type VercelSurfaceHostProps, VercelSurfaceSnapshotHost, type VercelSurfaceSnapshotHostProps, chatbotDictionary, configureA2uiMarkdown, createLocalStorageRuntimeConfigStore, createMemoryRuntimeConfigStore, isLlmEntryUsable, pickUsableLlmEntry, probeA2uiAvailability, probeOpenUiAvailability, sandboxExecutorDeps, useT };
package/dist/index.js CHANGED
@@ -7678,7 +7678,7 @@ function SheetContent({ children, className = "", side = "right", title, size, h
7678
7678
  const gutterClass = side === "left" ? "left-full" : "right-full";
7679
7679
  return /* @__PURE__ */ jsxs(DialogPortal, {
7680
7680
  container: usePortalContainer(),
7681
- children: [/* @__PURE__ */ jsx(DialogOverlay, { className: "fixed inset-0 z-[var(--webskill-z-scrim)] bg-ink/35 backdrop-blur-[1px]" }), /* @__PURE__ */ jsxs(DialogContent, {
7681
+ children: [/* @__PURE__ */ jsx(DialogOverlay, { className: "fixed inset-0 z-[var(--webskill-z-scrim)] bg-scrim backdrop-blur-[1px]" }), /* @__PURE__ */ jsxs(DialogContent, {
7682
7682
  className: `webskill-sheet-content fixed z-[var(--webskill-z-sheet)] flex flex-col bg-surface text-ink shadow-pop outline-none ${positionClass} ${className}`,
7683
7683
  style: size === void 0 ? style : {
7684
7684
  ...style,
@@ -7730,7 +7730,7 @@ function Dialog({ open, onClose, title, description, footer, hideCloseButton = f
7730
7730
  onOpenChange: (nextOpen) => !nextOpen && onClose(),
7731
7731
  children: /* @__PURE__ */ jsxs(DialogPortal, {
7732
7732
  container: usePortalContainer(),
7733
- children: [/* @__PURE__ */ jsx(DialogOverlay, { className: "fixed inset-0 z-[var(--webskill-z-scrim)] bg-foreground/45" }), /* @__PURE__ */ jsxs(DialogContent, {
7733
+ children: [/* @__PURE__ */ jsx(DialogOverlay, { className: "fixed inset-0 z-[var(--webskill-z-scrim)] bg-scrim" }), /* @__PURE__ */ jsxs(DialogContent, {
7734
7734
  className: `webskill-dialog-content fixed top-1/2 left-1/2 z-[var(--webskill-z-dialog)] flex max-h-[calc(100dvh-2rem)] -translate-x-1/2 -translate-y-1/2 flex-col rounded-card border border-border bg-surface shadow-pop outline-none max-sm:top-0 max-sm:left-0 max-sm:h-dvh max-sm:max-h-dvh max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-none ${className}`,
7735
7735
  ...size !== void 0 ? { style: { "--webskill-dialog-width": DIALOG_WIDTHS[size] } } : {},
7736
7736
  children: [
@@ -12014,7 +12014,7 @@ function AlertDialog({ open, title, description, footer, className = "", size, c
12014
12014
  open,
12015
12015
  children: /* @__PURE__ */ jsxs(Portal2, {
12016
12016
  container: usePortalContainer(),
12017
- children: [/* @__PURE__ */ jsx(Overlay2, { className: "fixed inset-0 z-[var(--webskill-z-scrim)] bg-foreground/45" }), /* @__PURE__ */ jsxs(Content2, {
12017
+ children: [/* @__PURE__ */ jsx(Overlay2, { className: "fixed inset-0 z-[var(--webskill-z-scrim)] bg-scrim" }), /* @__PURE__ */ jsxs(Content2, {
12018
12018
  onEscapeKeyDown: (event) => event.preventDefault(),
12019
12019
  className: `webskill-dialog-content fixed top-1/2 left-1/2 z-[var(--webskill-z-dialog)] flex max-h-[calc(100dvh-2rem)] -translate-x-1/2 -translate-y-1/2 flex-col rounded-card border border-border bg-surface shadow-pop outline-none ${className}`,
12020
12020
  ...size !== void 0 ? { style: { "--webskill-dialog-width": DIALOG_WIDTHS[size] } } : {},
@@ -43213,6 +43213,39 @@ const fadeInUp = {
43213
43213
  }
43214
43214
  };
43215
43215
 
43216
+ //#endregion
43217
+ //#region ../runtime/src/tools/types.ts
43218
+ /**
43219
+ * `file` 分片的上限(FR-23.4),量的是**实际上线的 base64 长度**而不是解码后的字节——
43220
+ * provider 的限额算的是请求载荷。
43221
+ *
43222
+ * 2026-08-14 查证的三家上限:
43223
+ * | provider | 上限 | 出处 |
43224
+ * | --------- | --------------------------------------- | ----------------------- |
43225
+ * | Anthropic | **32 MB**(**整个请求载荷**)、600 页 | PDF support / 请求大小 |
43226
+ * | OpenAI | 单文件 50 MB,全部文件合计 50 MB | File inputs / 使用须知 |
43227
+ * | Gemini | 50 MB 或 1000 页(内联与 Files API 同) | 文档理解 / 技术详情 |
43228
+ *
43229
+ * 取最小值 Anthropic 的 32 MB。它是**整个请求**的额度,不是文档单独的额度,
43230
+ * 所以再留出余量给系统提示词、catalog(约 34 KB)、历史消息与其余分片。
43231
+ * 25 000 KB base64 ≈ 18.75 MB 原始 PDF。取 1024 的整数倍:设置界面按 KB 展示,
43232
+ * 十进制的 24 000 000 会显示成 23437.5 这种读不出来的数。
43233
+ */
43234
+ const DEFAULT_MAX_DOCUMENT_BYTES = 256e5;
43235
+ /**
43236
+ * `document-text` 分片的上限(FR-23.4)。
43237
+ *
43238
+ * 约束来自**上下文窗口**而不是请求大小:抽出来的文本要整段进上下文。
43239
+ * 按英文约 4 字节/token 折算,500 KB ≈ 128K token,占 200K 窗口的多半,
43240
+ * 给历史消息与模型的回答留下其余。
43241
+ */
43242
+ const DEFAULT_MAX_DOCUMENT_TEXT_BYTES = 512e3;
43243
+ /**
43244
+ * 单次 fetchData 结果的字节上限(分册 16,FR-16.6):1000 KB。
43245
+ * 结构化数据不是文档,量级差一个数量级;超出**只拒不截**——截断的 JSON 解不出来。
43246
+ */
43247
+ const DEFAULT_MAX_DATA_SOURCE_BYTES = 1024e3;
43248
+
43216
43249
  //#endregion
43217
43250
  //#region ../runtime/src/engine/limits.ts
43218
43251
  /**
@@ -43256,7 +43289,9 @@ function defaultRuntimeConfig() {
43256
43289
  loop: {
43257
43290
  ...DEFAULT_LOOP_LIMITS$1,
43258
43291
  toolResultMaxBytes: 256e3,
43259
- maxHistoryMessages: 100
43292
+ maxHistoryMessages: 100,
43293
+ maxDocumentBytes: DEFAULT_MAX_DOCUMENT_BYTES,
43294
+ maxDocumentTextBytes: DEFAULT_MAX_DOCUMENT_TEXT_BYTES
43260
43295
  },
43261
43296
  interaction: {
43262
43297
  missingParams: "user",
@@ -43276,9 +43311,12 @@ function defaultRuntimeConfig() {
43276
43311
  networkPolicy: "deny-all",
43277
43312
  capabilities: {
43278
43313
  readReference: true,
43314
+ readAsset: true,
43279
43315
  writeArtifact: true,
43280
- confirm: true
43316
+ confirm: true,
43317
+ fetchData: false
43281
43318
  },
43319
+ maxDataSourceBytes: DEFAULT_MAX_DATA_SOURCE_BYTES,
43282
43320
  remoteUrl: {
43283
43321
  allowHttp: false,
43284
43322
  allowPrivateHosts: false
@@ -43306,9 +43344,10 @@ function defaultRuntimeConfig() {
43306
43344
  maxImageBytes: 10 * 1024 * 1024,
43307
43345
  maxImagesPerMessage: 5
43308
43346
  },
43347
+ documentSurface: { enabled: false },
43309
43348
  userProfile: {
43310
43349
  enabled: false,
43311
- injectMaxBytes: 4e3,
43350
+ injectMaxBytes: 4096,
43312
43351
  recordLimit: 500,
43313
43352
  encrypted: true
43314
43353
  },
@@ -43394,6 +43433,7 @@ function mergeRuntimeConfigDefaults(partial) {
43394
43433
  security: sub("security", d.security),
43395
43434
  appearance: mergeAppearance(p["appearance"], d.appearance),
43396
43435
  multimodal: mergeMultimodal(p["multimodal"], d.multimodal),
43436
+ documentSurface: mergeDocumentSurface(p["documentSurface"], d.documentSurface),
43397
43437
  userProfile: mergeUserProfile(p["userProfile"], d.userProfile),
43398
43438
  skillState: mergeSkillState(p["skillState"], d.skillState),
43399
43439
  dismissedAutoEntries: readStringList$1(p["dismissedAutoEntries"], [])
@@ -43429,6 +43469,14 @@ function mergeMultimodal(raw, d) {
43429
43469
  maxImagesPerMessage: positive(p["maxImagesPerMessage"], d.maxImagesPerMessage)
43430
43470
  };
43431
43471
  }
43472
+ /**
43473
+ * CSP 白名单只收字符串数组;存储被污染时回退默认值(空),
43474
+ * **不能**把非法值原样带进 CSP —— `viewerCspHeader` 会抛,等于整条投放面瘫掉。
43475
+ */
43476
+ function mergeDocumentSurface(raw, d) {
43477
+ const p = typeof raw === "object" && raw !== null ? raw : {};
43478
+ return { enabled: typeof p["enabled"] === "boolean" ? p["enabled"] : d.enabled };
43479
+ }
43432
43480
  /** 与 multimodal 同一套判据:非正数上限会让记录/注入通道恒空,回退默认值 */
43433
43481
  function mergeUserProfile(raw, d) {
43434
43482
  const p = typeof raw === "object" && raw !== null ? raw : {};
@@ -43563,6 +43611,8 @@ function createMemoryRuntimeConfigStore(initial) {
43563
43611
  /**
43564
43612
  * 条目未显式标注时:`image` 按「不支持」(推断表覆盖不到私有部署的模型名),
43565
43613
  * `tools` 按「支持」(只有显式写 false 才降级,否则升级后的存量配置会突然不能用技能)
43614
+ * `document` 同 `image` 按「不支持」:猜错的代价是把 PDF 发给纯文本模型,
43615
+ * 用户拿到的是 provider 的原始报错(FR-23.5)。
43566
43616
  * @experimental
43567
43617
  */
43568
43618
  function entryCapabilities(entry) {
@@ -44134,8 +44184,10 @@ const chatbotDictionary = defineDictionary({
44134
44184
  "composer.attachment.kind.text": "Text",
44135
44185
  "composer.attachment.kind.image": "Image",
44136
44186
  "composer.attachment.kind.file": "File",
44187
+ "composer.attachment.kind.document-text": "Doc text",
44137
44188
  "composer.attachment.compressed": "Compressed {from} → {to}",
44138
44189
  "composer.model": "Model",
44190
+ "composer.model.unconfigured": "Not usable: this entry has no API key or base URL. Configure it in the console.",
44139
44191
  "composer.model.demo": "Demo model",
44140
44192
  "composer.noModel.title": "No model configured",
44141
44193
  "composer.noModel.description": "A large language model has not been configured yet. Configure one on the model settings page before chatting.",
@@ -44213,7 +44265,7 @@ const chatbotDictionary = defineDictionary({
44213
44265
  "settings.agent.todo": "Task list",
44214
44266
  "settings.agent.todoNote": "Lets the model split a multi-step request into a checklist and report progress.",
44215
44267
  "settings.agent.generativeUi": "Generative UI",
44216
- "settings.agent.generativeUiNote": "Gates only the model’s own render_ui tool: letting the model start a screen on its own, picking the components and layout. Off by default: the component catalogue adds ~14 KB per request.",
44268
+ "settings.agent.generativeUiNote": "Gates only the model’s own render_ui tool: letting the model start a screen on its own, picking the components and layout. Off by default: the component catalogue adds ~34 KB per request.",
44217
44269
  "settings.agent.generativeUiUnaffected": "Turning this off does not hide skill-provided screens, interaction cards, or built-in result blocks.",
44218
44270
  "settings.agent.skillGeneration": "Skill generation",
44219
44271
  "settings.agent.skillGenerationNote": "Lets the model turn a reusable procedure from this conversation into a skill candidate. You confirm each one, and a reviewer approves it before anyone can use it.",
@@ -44471,8 +44523,10 @@ const chatbotDictionary = defineDictionary({
44471
44523
  "composer.attachment.kind.text": "文本",
44472
44524
  "composer.attachment.kind.image": "图片",
44473
44525
  "composer.attachment.kind.file": "文件",
44526
+ "composer.attachment.kind.document-text": "文档正文",
44474
44527
  "composer.attachment.compressed": "已压缩 {from} → {to}",
44475
44528
  "composer.model": "模型",
44529
+ "composer.model.unconfigured": "不可用:该条目既没有 API 密钥也没有基础地址,请在控制台补全。",
44476
44530
  "composer.model.demo": "演示模型",
44477
44531
  "composer.noModel.title": "尚未配置大模型",
44478
44532
  "composer.noModel.description": "大模型尚未配置,请先前往大模型配置页面完成配置,再开始对话。",
@@ -44550,7 +44604,7 @@ const chatbotDictionary = defineDictionary({
44550
44604
  "settings.agent.todo": "待办清单",
44551
44605
  "settings.agent.todoNote": "允许模型把多步骤任务拆成清单,并在执行中回报进度。",
44552
44606
  "settings.agent.generativeUi": "生成式 UI",
44553
- "settings.agent.generativeUiNote": "仅控制模型自己的 render_ui 工具:允许模型自行发起界面、自选组件与布局。默认关闭:组件目录会给每次请求增加约 14 KB。",
44607
+ "settings.agent.generativeUiNote": "仅控制模型自己的 render_ui 工具:允许模型自行发起界面、自选组件与布局。默认关闭:组件目录会给每次请求增加约 34 KB。",
44554
44608
  "settings.agent.generativeUiUnaffected": "关掉它不会隐藏技能自带的界面、交互卡,以及内建的结果块。",
44555
44609
  "settings.agent.skillGeneration": "技能自动生成",
44556
44610
  "settings.agent.skillGenerationNote": "允许模型把本次对话里可复用的做法整理成技能候选。每个候选都需你当场确认,且经审核通过后才能被使用。",
@@ -44657,7 +44711,7 @@ const DISPLAY_PHASE = {
44657
44711
  * 归并后追加一个相位;同相位连续到达时返回原数组本身,
44658
44712
  * 调用方据引用相等判断「没变化」,不必再做深比较。
44659
44713
  */
44660
- function appendRunPhase(phases, phase, at, note) {
44714
+ function appendRunPhase(phases, phase, at, note, detail) {
44661
44715
  const display = DISPLAY_PHASE[phase];
44662
44716
  const last = phases.at(-1);
44663
44717
  if (last?.phase === display) return phases;
@@ -44667,7 +44721,8 @@ function appendRunPhase(phases, phase, at, note) {
44667
44721
  }] : [], {
44668
44722
  phase: display,
44669
44723
  at,
44670
- ...note !== void 0 ? { note } : {}
44724
+ ...note !== void 0 ? { note } : {},
44725
+ ...detail !== void 0 ? { detail } : {}
44671
44726
  }];
44672
44727
  }
44673
44728
  /** 相位摘要:唯一读 `data` 的地方,此处事件还带着 `phase` 判别,下游不必再猜键名 */
@@ -44681,6 +44736,10 @@ function phaseNote(event) {
44681
44736
  default: return;
44682
44737
  }
44683
44738
  }
44739
+ /** 失败相位的具体说明(`reason` 之外的那句话)。只有 fail 有,其余相位一律 undefined。 */
44740
+ function phaseDetail(event) {
44741
+ return event.phase === "fail" ? event.data.detail : void 0;
44742
+ }
44684
44743
 
44685
44744
  //#endregion
44686
44745
  //#region src/core/title.ts
@@ -44794,8 +44853,10 @@ const CHATBOT_PROFILE_USER_ID = "local";
44794
44853
  * 终止原因 → 稳定错误码(FR-21.2)。不再全部压平为 RUN_FAILED:
44795
44854
  * “跑超时了”与“模型没配对”对用户是两件事,得给不同的下一步。
44796
44855
  */
44797
- const runFailureCode = (reason) => {
44798
- switch (reason) {
44856
+ const runFailureCode = (run) => {
44857
+ const traced = ((run.trace?.find((event) => event.type === "run.failed"))?.data)?.code;
44858
+ if (typeof traced === "string") return traced;
44859
+ switch (run.terminationReason) {
44799
44860
  case "timeout": return "RUN_TIMEOUT";
44800
44861
  case "interaction-timeout": return "RUN_INTERACTION_TIMEOUT";
44801
44862
  case "max-turns": return "RUN_MAX_TURNS_EXCEEDED";
@@ -44865,8 +44926,14 @@ const SANDBOX_MODE = {
44865
44926
  "blob-worker": "worker",
44866
44927
  "iframe-sandbox": "iframe"
44867
44928
  };
44868
- /** 条目能否真的发请求:云端要有端点或密钥,浏览器内置两者都不需要 */
44869
- const isUsableEntry = (entry) => entry.provider === "chrome-builtin" || Boolean(entry.apiKey?.trim()) || Boolean(entry.baseUrl?.trim());
44929
+ /**
44930
+ * 条目能否真的发请求:云端要有端点或密钥,浏览器内置两者都不需要。
44931
+ *
44932
+ * 导出给 UI:选择器不用同一份判据的话,用户能选中一个空配置条目,
44933
+ * 而引擎静默回退到另一条——界面说在用 A、实际跑的是 B(UI-UX8 D1)。
44934
+ * @experimental
44935
+ */
44936
+ const isLlmEntryUsable = (entry) => entry.provider === "chrome-builtin" || Boolean(entry.apiKey?.trim()) || Boolean(entry.baseUrl?.trim());
44870
44937
  /**
44871
44938
  * 选中条目 → 默认条目 → 首条;命中不可用条目时**回退到下一个可用条目**
44872
44939
  * (0.10.0 复核:选中项失效时宁可静默换用可用模型,也不能把 UI 锁死成
@@ -44883,7 +44950,7 @@ function pickUsableLlmEntry(entries, defaultId, selectedId) {
44883
44950
  byId(selectedId),
44884
44951
  byId(defaultId),
44885
44952
  ...entries
44886
- ].find((entry) => entry !== void 0 && isUsableEntry(entry));
44953
+ ].find((entry) => entry !== void 0 && isLlmEntryUsable(entry));
44887
44954
  }
44888
44955
  /** 选中条目 → 默认条目 → 首条;都没有则返回 undefined(走内置演示) */
44889
44956
  function pickLlmEntry(rc, selectedId) {
@@ -45401,12 +45468,13 @@ var ChatEngine = class {
45401
45468
  for (const attachment of attachments) {
45402
45469
  const path = `${this.attachmentsRoot}/${attachment.path}`;
45403
45470
  if (!await this.#adapter.storage.exists(path)) continue;
45404
- if (attachment.kind === "text") {
45471
+ if (attachment.kind === "text" || attachment.kind === "document-text") {
45405
45472
  const body = await this.#adapter.storage.readText(path);
45406
45473
  const clipped = body.length > ATTACHMENT_TEXT_LIMIT ? `${body.slice(0, ATTACHMENT_TEXT_LIMIT)}\n[truncated]` : body;
45474
+ const label = attachment.kind === "document-text" ? `${attachment.name} (${attachment.contentType}, body text only)` : `${attachment.name} (${attachment.contentType})`;
45407
45475
  parts.push({
45408
45476
  type: "text",
45409
- text: `--- Attachment: ${attachment.name} (${attachment.contentType}) ---\n${clipped}`
45477
+ text: `--- Attachment: ${label} ---\n${clipped}`
45410
45478
  });
45411
45479
  continue;
45412
45480
  }
@@ -45727,6 +45795,7 @@ var ChatEngine = class {
45727
45795
  toolTimeoutMs: rc.loop.toolTimeoutMs,
45728
45796
  ...rc.loop.temperature !== void 0 ? { temperature: rc.loop.temperature } : {},
45729
45797
  toolResultMaxBytes: rc.loop.toolResultMaxBytes,
45798
+ remoteUrl: rc.sandbox.remoteUrl,
45730
45799
  ...rc.renderResult ? {} : { renderResult: "off" }
45731
45800
  } : {},
45732
45801
  ...entryCapabilities(pickLlmEntry(rc, this.#selectedModelId)).tools ? {} : { toolCallingDisabled: true },
@@ -45792,7 +45861,11 @@ var ChatEngine = class {
45792
45861
  skillProviders: pageSkillSource ? [pageSkillSource] : [],
45793
45862
  ...this.#governanceDeps(),
45794
45863
  ...Object.keys(loopConfig).length > 0 ? { config: loopConfig } : {},
45795
- ...model ? { model } : {}
45864
+ ...model ? { model } : {},
45865
+ ...this.#options.fetchData ? { fetchData: this.#options.fetchData } : {},
45866
+ ...this.#adapter.linkedDocuments ? { linkedDocuments: this.#adapter.linkedDocuments } : {},
45867
+ ...this.#adapter.docxExtractor ? { docxExtractor: this.#adapter.docxExtractor } : {},
45868
+ ...this.#adapter.documentAudit ? { documentAudit: this.#adapter.documentAudit } : {}
45796
45869
  };
45797
45870
  const runtime = new WebSkillRuntime({
45798
45871
  ...deps,
@@ -45827,7 +45900,7 @@ var ChatEngine = class {
45827
45900
  const toolData = event.phase === "execute" && event.data.kind === "tool" ? event.data : void 0;
45828
45901
  if (!toolData) {
45829
45902
  const at = Date.parse(event.ts);
45830
- this.#runPhases.set(event.runId, appendRunPhase(this.#runPhases.get(event.runId) ?? [], event.phase, at, phaseNote(event)));
45903
+ this.#runPhases.set(event.runId, appendRunPhase(this.#runPhases.get(event.runId) ?? [], event.phase, at, phaseNote(event), phaseDetail(event)));
45831
45904
  this.#emit({
45832
45905
  type: "lifecycle",
45833
45906
  runId: event.runId,
@@ -45939,7 +46012,7 @@ var ChatEngine = class {
45939
46012
  status,
45940
46013
  output: result.output,
45941
46014
  activeSkillNames: run.activeSkillNames,
45942
- ...status === "completed" ? {} : { code: runFailureCode(run.terminationReason) }
46015
+ ...status === "completed" ? {} : { code: runFailureCode(run) }
45943
46016
  });
45944
46017
  this.#emit({
45945
46018
  type: "message-complete",
@@ -46545,7 +46618,10 @@ function terminationParts(phases) {
46545
46618
  return [{
46546
46619
  type: "data",
46547
46620
  name: TERMINATION_PART,
46548
- data: { ...last.note !== void 0 ? { reason: last.note } : {} }
46621
+ data: {
46622
+ ...last.note !== void 0 ? { reason: last.note } : {},
46623
+ ...last.detail !== void 0 ? { detail: last.detail } : {}
46624
+ }
46549
46625
  }];
46550
46626
  }
46551
46627
  /**
@@ -47138,7 +47214,7 @@ function InteractionWaitBar({ bridge, label, elsewhere = false, panel, onLocate
47138
47214
  id: INTERACTION_WAIT_ID,
47139
47215
  "data-testid": "interaction-wait",
47140
47216
  role: "status",
47141
- className: "mx-auto flex w-full max-w-3xl items-center gap-2 px-4 pt-2 text-xs text-muted",
47217
+ className: "webskill-chat-prose mx-auto flex w-full items-center gap-2 px-4 pt-2 text-xs text-muted",
47142
47218
  children: [
47143
47219
  /* @__PURE__ */ jsx(Hourglass, {
47144
47220
  className: "size-3.5 shrink-0",
@@ -47290,6 +47366,7 @@ function ModelMenuButton({ models }) {
47290
47366
  "data-model-option": option.id,
47291
47367
  checked: option.id === models.value,
47292
47368
  disabled: option.disabled === true,
47369
+ ...option.unconfigured === true ? { title: t("composer.model.unconfigured") } : {},
47293
47370
  onCheckedChange: () => models.onChange(option.id),
47294
47371
  children: /* @__PURE__ */ jsx("span", {
47295
47372
  className: "truncate",
@@ -47314,7 +47391,7 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
47314
47391
  className: "webskill-aui-composer shrink-0 px-4 py-4",
47315
47392
  children: /* @__PURE__ */ jsxs(ComposerPrimitive.AttachmentDropzone, {
47316
47393
  disabled: !attachments,
47317
- className: "webskill-aui-composer-shell mx-auto flex w-full max-w-3xl flex-col gap-2 px-3 py-2 transition-colors data-[dragging=true]:border-ring",
47394
+ 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",
47318
47395
  children: [
47319
47396
  /* @__PURE__ */ jsx("div", {
47320
47397
  "aria-label": t("composer.attachments"),
@@ -47886,18 +47963,29 @@ const TERMINATION_KEY = {
47886
47963
  /**
47887
47964
  * 非正常终态提示(FR-20.5)。停在消息顶层,不进思维链分组——
47888
47965
  * 那个容器在 run 结束后自动折叠且折叠时从 DOM 卸载,提示放进去等于没提示(裁决 D-9)。
47966
+ *
47967
+ * `reason` 只有七种取值,说不出「哪个上限、哪个模型、本轮带了文档」——
47968
+ * 那句话在 `RuntimeRun.error.message` 里,此前被整个丢掉(UI-UX8 D15)。
47889
47969
  */
47890
47970
  function TerminationNotice({ data }) {
47891
47971
  const t = useT();
47892
47972
  const reason = readString(data, "reason");
47973
+ const detail = readString(data, "detail");
47893
47974
  const key = reason !== void 0 && reason in TERMINATION_KEY ? TERMINATION_KEY[reason] : "run.terminated.unknown";
47894
- return /* @__PURE__ */ jsxs("p", {
47975
+ return /* @__PURE__ */ jsxs("div", {
47895
47976
  "data-webskill-termination": reason ?? "unknown",
47896
- className: "flex items-center gap-2 text-xs text-danger",
47977
+ className: "flex items-start gap-2 text-xs text-danger",
47897
47978
  children: [/* @__PURE__ */ jsx(OctagonX, {
47898
47979
  className: "size-3.5 shrink-0",
47899
47980
  "aria-hidden": true
47900
- }), /* @__PURE__ */ jsx("span", { children: t(key) })]
47981
+ }), /* @__PURE__ */ jsxs("div", {
47982
+ className: "flex min-w-0 flex-col gap-1",
47983
+ children: [/* @__PURE__ */ jsx("span", { children: t(key) }), detail !== void 0 && detail !== "" ? /* @__PURE__ */ jsx("span", {
47984
+ "data-testid": "termination-detail",
47985
+ className: "break-words text-muted",
47986
+ children: detail
47987
+ }) : null]
47988
+ })]
47901
47989
  });
47902
47990
  }
47903
47991
  function ToolGroup({ count, running, children }) {
@@ -47992,14 +48080,22 @@ function ToolRow({ part }) {
47992
48080
  ]
47993
48081
  }), open && (part.argsText !== "" || errorMessage !== void 0) ? /* @__PURE__ */ jsxs("div", {
47994
48082
  className: "border-t border-border px-2 py-1.5",
47995
- children: [errorMessage !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
47996
- className: "text-xs text-muted italic",
47997
- children: t(errorCode !== void 0 ? ERROR_TITLE_KEYS[errorCode] ?? "tool.failed.reason" : "tool.failed.reason")
47998
- }), /* @__PURE__ */ jsx("p", {
47999
- "data-testid": "tool-error-reason",
48000
- className: "text-xs break-words text-ink",
48001
- children: errorMessage
48002
- })] }) : null, part.argsText !== "" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
48083
+ children: [errorMessage !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
48084
+ /* @__PURE__ */ jsx("p", {
48085
+ className: "text-xs text-muted italic",
48086
+ children: t(errorCode !== void 0 ? ERROR_TITLE_KEYS[errorCode] ?? "tool.failed.reason" : "tool.failed.reason")
48087
+ }),
48088
+ /* @__PURE__ */ jsx("p", {
48089
+ "data-testid": "tool-error-reason",
48090
+ className: "text-xs break-words text-ink",
48091
+ children: errorMessage
48092
+ }),
48093
+ errorCode !== void 0 ? /* @__PURE__ */ jsx("p", {
48094
+ "data-testid": "tool-error-code",
48095
+ className: "mt-1 font-mono text-xs text-muted",
48096
+ children: errorCode
48097
+ }) : null
48098
+ ] }) : null, part.argsText !== "" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
48003
48099
  className: "text-xs text-muted italic",
48004
48100
  children: t("tool.args")
48005
48101
  }), /* @__PURE__ */ jsx("pre", {
@@ -48063,14 +48159,22 @@ function WorkingIndicator() {
48063
48159
  })
48064
48160
  });
48065
48161
  }
48066
- /** data part → 只读交互卡;请求体是落盘原样,畸形数据直接不渲染而不是抛 */
48162
+ /**
48163
+ * data part → 只读交互卡;请求体是落盘原样,畸形数据直接不渲染而不是抛。
48164
+ *
48165
+ * surfaceId 必须掺入 useId:交互 id 是 run 内序号,不同 run 的第一个交互都叫 `int-1`。
48166
+ * 两张 submitted 卡同屏时若直接用 `interaction-${id}`,surfaceForm 的控件 id 与
48167
+ * `<label htmlFor>` 关联会串到另一张卡(UI-UX8 UX-03:把上一张卡的问题念成这张卡的)。
48168
+ * @internal
48169
+ */
48067
48170
  function PersistedInteraction({ data }) {
48171
+ const domId = useId();
48068
48172
  const request = data["request"];
48069
48173
  const id = readString(data, "id");
48070
48174
  if (typeof request !== "object" || request === null || id === void 0) return null;
48071
48175
  const values = data["submittedValues"];
48072
48176
  return /* @__PURE__ */ jsx(SubmittedInteraction, {
48073
- surfaceId: `interaction-${id}`,
48177
+ surfaceId: `interaction-${id}-${domId}`,
48074
48178
  request,
48075
48179
  ...values === void 0 ? {} : { submittedValues: values }
48076
48180
  });
@@ -58627,10 +58731,13 @@ function WebSkillMessageExtensions({ message, onDownload, surfaceRegistry, rende
58627
58731
  runId: message.runId ?? "",
58628
58732
  ...onDownload ? { onDownload } : {}
58629
58733
  }) : null,
58630
- renderable.length > 0 ? /* @__PURE__ */ jsx(HistoricalSurfaces, {
58631
- snapshots: renderable,
58632
- ...renderer ? { renderer } : {},
58633
- ...surfaceRegistry ? { registry: surfaceRegistry } : {}
58734
+ renderable.length > 0 ? /* @__PURE__ */ jsx("div", {
58735
+ className: "webskill-chat-surface-wide",
58736
+ children: /* @__PURE__ */ jsx(HistoricalSurfaces, {
58737
+ snapshots: renderable,
58738
+ ...renderer ? { renderer } : {},
58739
+ ...surfaceRegistry ? { registry: surfaceRegistry } : {}
58740
+ })
58634
58741
  }) : null,
58635
58742
  renderable.length < surfaces.length ? /* @__PURE__ */ jsx("p", {
58636
58743
  "data-testid": "legacy-surface-placeholder",
@@ -58833,6 +58940,14 @@ const IMAGE_MIME_SET = new Set(IMAGE_MIME_TYPES);
58833
58940
  /** 非文本非图片但仍可整体外发的类型(provider 的 file 分片) */
58834
58941
  const FILE_MIME_TYPES = ["application/pdf"];
58835
58942
  const FILE_MIME_SET = new Set(FILE_MIME_TYPES);
58943
+ /**
58944
+ * 需要**客户端先抽取成文本**才能外发的类型(FR-23.9)。
58945
+ * 不走直通的 `file`:docx 发给 provider 会被当成二进制垃圾。
58946
+ */
58947
+ const EXTRACTED_MIME_TYPES = ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"];
58948
+ const EXTRACTED_MIME_SET = new Set(EXTRACTED_MIME_TYPES);
58949
+ const EXTRACTED_EXTENSIONS = ["docx"];
58950
+ const EXTRACTED_EXTENSION_SET = new Set(EXTRACTED_EXTENSIONS);
58836
58951
  function extensionOf(fileName) {
58837
58952
  return fileName.split(".").pop()?.toLowerCase() ?? "";
58838
58953
  }
@@ -58840,9 +58955,11 @@ function extensionOf(fileName) {
58840
58955
  function classifyAttachment(contentType, fileName) {
58841
58956
  const type = contentType.toLowerCase();
58842
58957
  if (IMAGE_MIME_SET.has(type)) return "image";
58958
+ if (EXTRACTED_MIME_SET.has(type)) return "document-text";
58843
58959
  if (FILE_MIME_SET.has(type)) return "file";
58844
58960
  if (type.startsWith("text/") || type === "application/json") return "text";
58845
58961
  if (type === "" && TEXT_EXTENSION_SET.has(extensionOf(fileName))) return "text";
58962
+ if (type === "" && EXTRACTED_EXTENSION_SET.has(extensionOf(fileName))) return "document-text";
58846
58963
  }
58847
58964
  const TEXT_ACCEPT = [
58848
58965
  "text/plain",
@@ -58859,6 +58976,7 @@ function attachmentAccept(options) {
58859
58976
  const parts = [...TEXT_ACCEPT, ...TEXT_EXTENSIONS.map((ext) => `.${ext}`)];
58860
58977
  if (options.images) parts.push(...IMAGE_MIME_TYPES);
58861
58978
  if (options.files) parts.push(...FILE_MIME_TYPES);
58979
+ if (options.documents) parts.push(...EXTRACTED_MIME_TYPES, ...EXTRACTED_EXTENSIONS.map((ext) => `.${ext}`));
58862
58980
  return parts.join(",");
58863
58981
  }
58864
58982
 
@@ -58891,7 +59009,11 @@ var WebSkillAttachmentAdapter = class {
58891
59009
  }
58892
59010
  /** assistant-ui 每次打开文件选择器都读一次,因此开关与模型能力的变化无需重建适配器 */
58893
59011
  get accept() {
58894
- return attachmentAccept({ images: this.#imagesAllowed() });
59012
+ return attachmentAccept({
59013
+ images: this.#imagesAllowed(),
59014
+ files: true,
59015
+ documents: this.#options.docxExtractor !== void 0
59016
+ });
58895
59017
  }
58896
59018
  #multimodal() {
58897
59019
  return this.#options.multimodal?.() ?? DEFAULT_MULTIMODAL;
@@ -58905,7 +59027,8 @@ var WebSkillAttachmentAdapter = class {
58905
59027
  if (kind === "image") {
58906
59028
  if (!this.#multimodal().imageAttachments) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "Image attachments are turned off. Enable them in settings before sending images.");
58907
59029
  if (!(this.#options.imageCapable?.() ?? false)) throw new WebSkillError("MODEL_IMAGE_UNSUPPORTED", "Current model does not support image input. Switch model or remove the image.");
58908
- } else if (file.size > MAX_BYTES) throw new WebSkillError("ATTACHMENT_TOO_LARGE", `Attachment "${file.name}" is ${Math.round(file.size / 1024 / 1024)} MB, which exceeds the 10 MB limit`);
59030
+ } else if (kind === "document-text" && this.#options.docxExtractor === void 0) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "Word documents are not supported in this environment: no docx extractor is wired up.");
59031
+ else if (file.size > MAX_BYTES) throw new WebSkillError("ATTACHMENT_TOO_LARGE", `Attachment "${file.name}" is ${Math.round(file.size / 1024 / 1024)} MB, which exceeds the 10 MB limit`);
58909
59032
  const id = `att-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
58910
59033
  const relative = `${this.#options.sessionId() ?? "pending"}/${id}-${safeName(file.name)}`;
58911
59034
  const path = `${this.#options.root}/${relative}`;
@@ -58946,6 +59069,15 @@ var WebSkillAttachmentAdapter = class {
58946
59069
  size: file.size
58947
59070
  };
58948
59071
  }
59072
+ if (kind === "document-text") {
59073
+ const extract = this.#options.docxExtractor;
59074
+ if (extract === void 0) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "No docx extractor is wired up in this environment.");
59075
+ await this.#options.storage.writeText(path, await extract(new Uint8Array(await file.arrayBuffer())));
59076
+ return {
59077
+ ...base,
59078
+ size: file.size
59079
+ };
59080
+ }
58949
59081
  if (kind === "file") {
58950
59082
  await this.#options.storage.writeBinary(path, new Uint8Array(await file.arrayBuffer()));
58951
59083
  return {
@@ -59023,7 +59155,7 @@ const start = (runId, at) => ({
59023
59155
  chunks: 0
59024
59156
  });
59025
59157
  function appendPhase(live, event) {
59026
- const next = appendRunPhase(live.phases, event.phase, event.at, phaseNote(event));
59158
+ const next = appendRunPhase(live.phases, event.phase, event.at, phaseNote(event), phaseDetail(event));
59027
59159
  return next === live.phases ? live : {
59028
59160
  ...live,
59029
59161
  phases: [...next]
@@ -59349,6 +59481,13 @@ function ActiveInteractionCard({ request, bridge }) {
59349
59481
  const pageAction = isAuthorize && request.capability === "pageAction" ? pageActionDetails(request.details) : void 0;
59350
59482
  const handleAction = (event) => {
59351
59483
  if (event.intent !== "submit") {
59484
+ if (request.type === "confirm" || request.type === "authorize") {
59485
+ bridge.resolve({
59486
+ id: request.id,
59487
+ value: false
59488
+ });
59489
+ return;
59490
+ }
59352
59491
  bridge.resolve({
59353
59492
  id: request.id,
59354
59493
  cancelled: true
@@ -59820,6 +59959,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
59820
59959
  ...config?.hooks ? { hooks: config.hooks } : {},
59821
59960
  ...config?.governance ? { governance: config.governance } : {},
59822
59961
  ...config?.skillCandidates ? { skillCandidates: config.skillCandidates } : {},
59962
+ ...config?.fetchData ? { fetchData: config.fetchData } : {},
59823
59963
  ...rendererProp ? { renderer: rendererProp } : {}
59824
59964
  }), [
59825
59965
  adapter,
@@ -59831,6 +59971,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
59831
59971
  config?.hooks,
59832
59972
  config?.governance,
59833
59973
  config?.skillCandidates,
59974
+ config?.fetchData,
59834
59975
  rendererProp
59835
59976
  ]);
59836
59977
  useEffect(() => {
@@ -59928,7 +60069,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
59928
60069
  root: engine.attachmentsRoot,
59929
60070
  sessionId: () => sessionRef.current,
59930
60071
  multimodal: () => multimodalRef.current,
59931
- imageCapable: () => imageCapableRef.current
60072
+ imageCapable: () => imageCapableRef.current,
60073
+ ...adapter.docxExtractor ? { docxExtractor: adapter.docxExtractor } : {}
59932
60074
  }), [adapter, engine]);
59933
60075
  useEffect(() => {
59934
60076
  let cancelled = false;
@@ -60000,13 +60142,19 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
60000
60142
  const composerModels = useMemo(() => {
60001
60143
  if (llmSelection.entries.length === 0) return void 0;
60002
60144
  const selected = modelId ?? llmSelection.defaultId ?? llmSelection.entries[0]?.id ?? "";
60145
+ const effective = pickUsableLlmEntry(llmSelection.entries, llmSelection.defaultId, modelId)?.id ?? selected;
60003
60146
  return {
60004
- options: llmSelection.entries.map((entry) => ({
60005
- id: entry.id,
60006
- label: entry.label,
60007
- ...entry.provider === "chrome-builtin" && chromeBuiltin?.available === false ? { disabled: true } : {}
60008
- })),
60009
- value: selected,
60147
+ options: llmSelection.entries.map((entry) => {
60148
+ const builtinUnavailable = entry.provider === "chrome-builtin" && chromeBuiltin?.available === false;
60149
+ const unusable = !isLlmEntryUsable(entry);
60150
+ return {
60151
+ id: entry.id,
60152
+ label: entry.label,
60153
+ ...builtinUnavailable || unusable ? { disabled: true } : {},
60154
+ ...unusable && !builtinUnavailable ? { unconfigured: true } : {}
60155
+ };
60156
+ }),
60157
+ value: effective,
60010
60158
  onChange: (id) => {
60011
60159
  setModelId(id);
60012
60160
  engine.setModel(id);
@@ -60356,7 +60504,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
60356
60504
  })
60357
60505
  })
60358
60506
  })] }), /* @__PURE__ */ jsxs("div", {
60359
- className: "flex min-h-0 min-w-0 flex-1 flex-col",
60507
+ className: "webskill-chat-column flex min-h-0 min-w-0 flex-1 flex-col",
60360
60508
  children: [
60361
60509
  /* @__PURE__ */ jsx(AssistantUiThreadToolbar, {
60362
60510
  ...config?.title ? { title: config.title } : {},
@@ -60374,7 +60522,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
60374
60522
  /* @__PURE__ */ jsx(AssistantUiViewport, {
60375
60523
  grow: !composerCentered,
60376
60524
  children: /* @__PURE__ */ jsxs("div", {
60377
- className: "mx-auto flex w-full min-w-0 max-w-3xl flex-col gap-4 px-4 py-6",
60525
+ className: "webskill-chat-prose mx-auto flex w-full min-w-0 flex-col gap-4 px-4 py-6",
60378
60526
  children: [
60379
60527
  messages.length === 0 && !sending ? /* @__PURE__ */ jsx(AssistantUiEmptyState, {
60380
60528
  ...config?.title ? { title: config.title } : {},
@@ -60424,24 +60572,29 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
60424
60572
  activeRunId: currentRunId,
60425
60573
  onDownload: handleDownload
60426
60574
  }),
60427
- renderer === "native" && liveSurfaceRunId ? /* @__PURE__ */ jsx(UiSurfaceList, {
60428
- bridge: engine.bridge,
60429
- runId: liveSurfaceRunId,
60430
- ...surfaceRegistry ? { registry: surfaceRegistry } : {}
60431
- }) : null,
60432
- renderer === "vercel" && liveSurfaceRunId ? /* @__PURE__ */ jsx(VercelSurfaceHost, {
60433
- bridge: engine.bridge,
60434
- runId: liveSurfaceRunId
60435
- }) : null,
60436
- renderer === "openui" && liveSurfaceRunId ? /* @__PURE__ */ jsx(OpenUiSurfaceHost, {
60437
- bridge: engine.bridge,
60438
- runId: liveSurfaceRunId
60439
- }) : null,
60440
- renderer === "a2ui" && liveSurfaceRunId ? /* @__PURE__ */ jsx(A2uiSurfaceHost, {
60441
- bridge: engine.bridge,
60442
- runId: liveSurfaceRunId,
60443
- ...surfaceRegistry ? { registry: surfaceRegistry } : {}
60444
- }) : null,
60575
+ /* @__PURE__ */ jsxs("div", {
60576
+ className: "webskill-chat-surface-wide",
60577
+ children: [
60578
+ renderer === "native" && liveSurfaceRunId ? /* @__PURE__ */ jsx(UiSurfaceList, {
60579
+ bridge: engine.bridge,
60580
+ runId: liveSurfaceRunId,
60581
+ ...surfaceRegistry ? { registry: surfaceRegistry } : {}
60582
+ }) : null,
60583
+ renderer === "vercel" && liveSurfaceRunId ? /* @__PURE__ */ jsx(VercelSurfaceHost, {
60584
+ bridge: engine.bridge,
60585
+ runId: liveSurfaceRunId
60586
+ }) : null,
60587
+ renderer === "openui" && liveSurfaceRunId ? /* @__PURE__ */ jsx(OpenUiSurfaceHost, {
60588
+ bridge: engine.bridge,
60589
+ runId: liveSurfaceRunId
60590
+ }) : null,
60591
+ renderer === "a2ui" && liveSurfaceRunId ? /* @__PURE__ */ jsx(A2uiSurfaceHost, {
60592
+ bridge: engine.bridge,
60593
+ runId: liveSurfaceRunId,
60594
+ ...surfaceRegistry ? { registry: surfaceRegistry } : {}
60595
+ }) : null
60596
+ ]
60597
+ }),
60445
60598
  error ? /* @__PURE__ */ jsx(ErrorCard, {
60446
60599
  message: error.message,
60447
60600
  ...error.code ? { code: error.code } : {},
@@ -60558,7 +60711,7 @@ function VercelPayloadPreview({ bridge }) {
60558
60711
  * Version of the published `@webskill/chatbot` package, injected at build time.
60559
60712
  * @stable
60560
60713
  */
60561
- const CHATBOT_VERSION = "0.7.0";
60714
+ const CHATBOT_VERSION = "0.8.0";
60562
60715
 
60563
60716
  //#endregion
60564
- 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, pickUsableLlmEntry, probeA2uiAvailability, probeOpenUiAvailability, sandboxExecutorDeps, useT };
60717
+ 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.7.0",
3
+ "version": "0.8.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.10.0",
29
+ "@webskill/sdk": "^0.11.0",
30
30
  "react": ">=19.0.0",
31
31
  "react-dom": ">=19.0.0",
32
32
  "zod": "^3.25.0 || ^4.0.0"