@webskill/sdk 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.
Files changed (35) hide show
  1. package/dist/agent.d.ts +1 -1
  2. package/dist/agent.js +1 -1
  3. package/dist/browser.d.ts +151 -4
  4. package/dist/browser.js +250 -25
  5. package/dist/{catalogComponents-Dr5dFMAb-DKH_7VPI.js → catalogComponents-DfxxfUvn-D55Gbb2l.js} +3435 -437
  6. package/dist/{dist-8oQRa8Xz.js → dist-59XlqDuv.js} +93 -6
  7. package/dist/{dist-DnYG2-eY.js → dist-CJqQsIm9.js} +498 -118
  8. package/dist/{dist-D0qW6e40.js → dist-DmI5SBBF.js} +192 -17
  9. package/dist/{eventTypes-DjIQpt8Y-Bj3vghj4.js → eventTypes-g1BXL6x5-CibcOftR.js} +7 -2
  10. package/dist/governance.d.ts +16 -3
  11. package/dist/governance.js +24 -5
  12. package/dist/{index-DkbABR43.d.ts → index-K-eewlGL.d.ts} +138 -75
  13. package/dist/{index-BwsK9lGk.d.ts → index-P9J2LTfU.d.ts} +163 -6
  14. package/dist/{index-Ba3xFtfz.d.ts → index-fLskQfAS.d.ts} +2 -2
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.js +4 -4
  17. package/dist/mcp.d.ts +8 -2
  18. package/dist/mcp.js +12 -2
  19. package/dist/{memoryArtifactStore-52Zn9npI-BMPYwvoy.js → memoryArtifactStore-52Zn9npI-upv5OWYf.js} +1 -1
  20. package/dist/node.d.ts +3 -3
  21. package/dist/node.js +3 -3
  22. package/dist/{openUiLibrary-Bdrji9qK-D2LxmM-a.js → openUiLibrary-DURlAxjk-CU6AzfSW.js} +3 -3
  23. package/dist/{skillVersionStore-Bl-ElD45-CWPvGvoq.d.ts → skillVersionStore-Bl-ElD45-gRfSaAby.d.ts} +1 -1
  24. package/dist/{testing-CYTFqkDm.js → testing-BCUO5gZR.js} +2 -2
  25. package/dist/testing.d.ts +1 -1
  26. package/dist/testing.js +2 -2
  27. package/dist/{types-CcxRLdJG-DCXyw1US.d.ts → types-B3n0cMZu-BdcqQ35O.d.ts} +46 -6
  28. package/dist/ui-react.d.ts +13 -6
  29. package/dist/ui-react.js +153 -84
  30. package/dist/ui-vue.d.ts +1 -1
  31. package/dist/ui-vue.js +2 -2
  32. package/dist/ui.d.ts +4 -4
  33. package/dist/ui.js +3 -3
  34. package/dist/{webskillLitCatalog-_mugzRHx-B_54vxum.js → webskillLitCatalog-DwTwSBFt-DiXXpNZA.js} +22 -3
  35. package/package.json +2 -2
@@ -1,7 +1,137 @@
1
- import { m as WebSkillError } from "./dist-8oQRa8Xz.js";
1
+ import { m as WebSkillError } from "./dist-59XlqDuv.js";
2
2
  import { toJSONSchema, z } from "zod";
3
3
 
4
- //#region ../ui/dist/webskillCatalog-B-IQxOKs.js
4
+ //#region ../ui/dist/webskillCatalog-CKM0bu47.js
5
+ const DEFAULT_INTERACTION_TEXTS = {
6
+ submit: "Submit",
7
+ cancel: "Cancel",
8
+ confirm: "Confirm",
9
+ select: "Select",
10
+ allow: "Allow",
11
+ deny: "Deny",
12
+ chooseFile: "Choose file",
13
+ decline: "Decline",
14
+ required: "This field is required",
15
+ suggested: "Suggested:",
16
+ useSuggestion: "Use"
17
+ };
18
+ function resolveInteractionTexts(texts) {
19
+ return texts ? {
20
+ ...DEFAULT_INTERACTION_TEXTS,
21
+ ...texts
22
+ } : DEFAULT_INTERACTION_TEXTS;
23
+ }
24
+ /** 可复用的部分取自 DEFAULT_INTERACTION_TEXTS,不另抄一份(AC-G10) */
25
+ const DEFAULT_SURFACE_FORM_TEXTS = {
26
+ required: DEFAULT_INTERACTION_TEXTS.required,
27
+ submit: DEFAULT_INTERACTION_TEXTS.submit,
28
+ cancel: DEFAULT_INTERACTION_TEXTS.cancel,
29
+ addItem: "Add",
30
+ removeItem: "Remove",
31
+ arrayFirstItemOnly: "This renderer shows only the first item of a repeatable group.",
32
+ readOnlySnapshot: "This is a saved record — its actions are no longer available."
33
+ };
34
+ function resolveSurfaceFormTexts(texts) {
35
+ return texts ? {
36
+ ...DEFAULT_SURFACE_FORM_TEXTS,
37
+ ...texts
38
+ } : DEFAULT_SURFACE_FORM_TEXTS;
39
+ }
40
+ /** 提交值按请求类型归形(WebFormBridge 与框架组件库共享单一来源) */
41
+ function shapeInteractionValue(model, values) {
42
+ switch (model.kind) {
43
+ case "ask": return values["answer"];
44
+ case "confirm": return values["confirmed"] === true;
45
+ case "select": return values["selected"];
46
+ case "authorize": return true;
47
+ case "file-pick": return values["file"];
48
+ case "form": return values;
49
+ }
50
+ }
51
+ /** 五类 InteractionRequest → 统一中间模型(框架无关) */
52
+ function interactionToFormModel(request, texts) {
53
+ const t = resolveInteractionTexts(texts);
54
+ switch (request.type) {
55
+ case "ask": return {
56
+ kind: "ask",
57
+ message: request.message,
58
+ controls: [{
59
+ name: "answer",
60
+ label: request.message,
61
+ control: "text",
62
+ required: true,
63
+ ...request.suggestion ? { suggestion: request.suggestion } : {}
64
+ }],
65
+ submitLabel: t.submit,
66
+ cancelLabel: t.cancel
67
+ };
68
+ case "confirm": return {
69
+ kind: "confirm",
70
+ message: request.message,
71
+ controls: [{
72
+ name: "confirmed",
73
+ label: request.message,
74
+ control: "boolean",
75
+ defaultValue: request.defaultValue ?? true
76
+ }],
77
+ submitLabel: t.confirm,
78
+ cancelLabel: t.cancel
79
+ };
80
+ case "form": return {
81
+ kind: "form",
82
+ ...request.title ? { title: request.title } : {},
83
+ controls: request.fields.map((f) => ({
84
+ name: f.name,
85
+ label: f.label,
86
+ control: f.type,
87
+ ...f.required ? { required: true } : {},
88
+ ...f.description ? { description: f.description } : {},
89
+ ...f.defaultValue !== void 0 ? { defaultValue: f.defaultValue } : {},
90
+ ...f.suggestion ? { suggestion: f.suggestion } : {},
91
+ ...f.options ? { options: f.options } : {}
92
+ })),
93
+ submitLabel: t.submit,
94
+ cancelLabel: t.cancel
95
+ };
96
+ case "select": return {
97
+ kind: "select",
98
+ message: request.message,
99
+ controls: [{
100
+ name: "selected",
101
+ label: request.message,
102
+ control: "select",
103
+ required: true,
104
+ options: request.options,
105
+ ...request.suggestion ? { suggestion: request.suggestion } : {}
106
+ }],
107
+ submitLabel: t.select,
108
+ cancelLabel: t.cancel
109
+ };
110
+ case "authorize": return {
111
+ kind: "authorize",
112
+ title: "Authorization required",
113
+ message: request.message,
114
+ controls: [],
115
+ submitLabel: t.allow,
116
+ cancelLabel: t.deny
117
+ };
118
+ case "file-pick": return {
119
+ kind: "file-pick",
120
+ title: "File requested",
121
+ message: request.message,
122
+ controls: [{
123
+ name: "file",
124
+ label: request.field ?? "file",
125
+ control: "file",
126
+ required: true,
127
+ ...request.accept ? { accept: request.accept } : {},
128
+ ...request.multiple ? { multiple: true } : {}
129
+ }],
130
+ submitLabel: t.chooseFile,
131
+ cancelLabel: t.decline
132
+ };
133
+ }
134
+ }
5
135
  /**
6
136
  * 最小子集 markdown → DOM:标题(#..###)、无序列表、代码块(```)、加粗、链接。
7
137
  * 全部内容经 textContent 写入,用户内容天然转义防 HTML 注入。
@@ -192,6 +322,139 @@ function mountEchart(container) {
192
322
  }
193
323
  };
194
324
  }
325
+ function isRecord$1$1(value) {
326
+ return typeof value === "object" && value !== null && !Array.isArray(value);
327
+ }
328
+ /**
329
+ * 逐节点降级(FR-23.3)。
330
+ *
331
+ * 与 `validate` 的关系见 `UiCatalog.sanitize` 的注释:一个整块拒绝、一个逐节点救。
332
+ *
333
+ * **不写回 `safeParse` 的结果**是刻意的:zod 会剥离未声明的键,
334
+ * 而渲染层确实在读若干未进 zod 的 prop(如 `suggestion`,由 SDK 侧下发而非模型生成)。
335
+ * 写回等于把它们悄悄删掉。这里只删「确实解析失败」的那几个键。
336
+ */
337
+ function sanitizeNode(catalog, byName, node, path, degradations) {
338
+ if (!isRecord$1$1(node)) {
339
+ degradations.push({
340
+ path,
341
+ kind: "node-dropped",
342
+ component: "unknown",
343
+ detail: "Node is not an object."
344
+ });
345
+ return;
346
+ }
347
+ const name = node["component"];
348
+ if (typeof name !== "string") {
349
+ degradations.push({
350
+ path,
351
+ kind: "node-dropped",
352
+ component: "unknown",
353
+ detail: "Node has no \"component\" name."
354
+ });
355
+ return;
356
+ }
357
+ const def = byName.get(name);
358
+ if (!def) {
359
+ degradations.push({
360
+ path,
361
+ kind: "unknown-component",
362
+ component: name,
363
+ detail: `Unknown component "${name}"; the catalog declares: ${catalog.components.map((c) => c.name).join(", ")}`
364
+ });
365
+ return;
366
+ }
367
+ const rawProps = isRecord$1$1(node["props"]) ? node["props"] : {};
368
+ let props = rawProps;
369
+ const parsed = def.props.safeParse(rawProps);
370
+ if (!parsed.success) {
371
+ const offending = new Set(parsed.error.issues.map((issue) => String(issue.path[0] ?? "")).filter((key) => key !== ""));
372
+ const kept = {};
373
+ for (const [key, value] of Object.entries(rawProps)) if (!offending.has(key)) kept[key] = value;
374
+ const retry = def.props.safeParse(kept);
375
+ if (!retry.success) {
376
+ degradations.push({
377
+ path,
378
+ kind: "node-dropped",
379
+ component: name,
380
+ detail: `Props cannot be repaired by dropping invalid keys: ${retry.error.issues.map((i) => i.message).join("; ")}`
381
+ });
382
+ return;
383
+ }
384
+ for (const key of offending) degradations.push({
385
+ path: `${path}.props.${key}`,
386
+ kind: "prop-dropped",
387
+ component: name,
388
+ detail: `Dropped invalid prop "${key}".`
389
+ });
390
+ props = kept;
391
+ }
392
+ const sanitized = {
393
+ component: name,
394
+ props
395
+ };
396
+ if (typeof node["id"] === "string") sanitized.id = node["id"];
397
+ const children = node["children"];
398
+ if (children === void 0) return sanitized;
399
+ if (!Array.isArray(children)) {
400
+ degradations.push({
401
+ path: `${path}.children`,
402
+ kind: "constraint-ignored",
403
+ component: name,
404
+ detail: "children is not an array; rendered without children."
405
+ });
406
+ return sanitized;
407
+ }
408
+ if (def.children === void 0) {
409
+ degradations.push({
410
+ path: `${path}.children`,
411
+ kind: "constraint-ignored",
412
+ component: name,
413
+ detail: `Component "${name}" is a leaf; its children were dropped.`
414
+ });
415
+ return sanitized;
416
+ }
417
+ const allowed = def.children;
418
+ const singletons = /* @__PURE__ */ new Map();
419
+ const kept = [];
420
+ children.forEach((child, index) => {
421
+ const childPath = `${path}.children[${index}]`;
422
+ const childName = isRecord$1$1(child) && typeof child["component"] === "string" ? child["component"] : void 0;
423
+ if (allowed !== "any" && childName !== void 0 && !allowed.includes(childName)) {
424
+ degradations.push({
425
+ path: childPath,
426
+ kind: byName.has(childName) ? "node-dropped" : "unknown-component",
427
+ component: childName,
428
+ detail: byName.has(childName) ? `Component "${name}" only accepts children: ${[...allowed].join(", ")}` : `Unknown component "${childName}"; the catalog declares: ${catalog.components.map((c) => c.name).join(", ")}`
429
+ });
430
+ return;
431
+ }
432
+ if (childName !== void 0 && byName.get(childName)?.singletonPerContainer === true) {
433
+ const seen = (singletons.get(childName) ?? 0) + 1;
434
+ singletons.set(childName, seen);
435
+ if (seen > 1) {
436
+ degradations.push({
437
+ path: childPath,
438
+ kind: "node-dropped",
439
+ component: childName,
440
+ detail: `At most one "${childName}" per container; the extra one was dropped.`
441
+ });
442
+ return;
443
+ }
444
+ }
445
+ const sanitizedChild = sanitizeNode(catalog, byName, child, childPath, degradations);
446
+ if (sanitizedChild) kept.push(sanitizedChild);
447
+ });
448
+ sanitized.children = kept;
449
+ return sanitized;
450
+ }
451
+ function sanitizeUiSpec(catalog, byName, spec) {
452
+ const degradations = [];
453
+ return {
454
+ node: sanitizeNode(catalog, byName, spec, "root", degradations),
455
+ degradations
456
+ };
457
+ }
195
458
  function componentSchema(def) {
196
459
  const props = toJSONSchema(def.props, { io: "input" });
197
460
  const node = {
@@ -220,7 +483,7 @@ function componentSchema(def) {
220
483
  };
221
484
  return node;
222
485
  }
223
- function isRecord$1(value) {
486
+ function isRecord$2(value) {
224
487
  return typeof value === "object" && value !== null && !Array.isArray(value);
225
488
  }
226
489
  function typeOf(schema) {
@@ -260,7 +523,7 @@ function promptFor(catalog, options) {
260
523
  return lines.join("\n").trimEnd();
261
524
  }
262
525
  function validateNode(catalog, byName, node, path, issues) {
263
- if (!isRecord$1(node)) {
526
+ if (!isRecord$2(node)) {
264
527
  issues.push({
265
528
  path,
266
529
  message: "Node must be an object"
@@ -308,7 +571,7 @@ function validateNode(catalog, byName, node, path, issues) {
308
571
  const singletons = /* @__PURE__ */ new Map();
309
572
  children.forEach((child, index) => {
310
573
  const childPath = `${path}.children[${index}]`;
311
- if (allowed !== "any" && isRecord$1(child) && typeof child["component"] === "string") {
574
+ if (allowed !== "any" && isRecord$2(child) && typeof child["component"] === "string") {
312
575
  if (!allowed.includes(child["component"])) {
313
576
  issues.push({
314
577
  path: childPath,
@@ -317,7 +580,7 @@ function validateNode(catalog, byName, node, path, issues) {
317
580
  return;
318
581
  }
319
582
  }
320
- if (isRecord$1(child) && typeof child["component"] === "string") {
583
+ if (isRecord$2(child) && typeof child["component"] === "string") {
321
584
  const childName = child["component"];
322
585
  if (byName.get(childName)?.singletonPerContainer === true) {
323
586
  const seen = (singletons.get(childName) ?? 0) + 1;
@@ -362,7 +625,8 @@ function defineUiCatalog(input) {
362
625
  ok: false,
363
626
  issues
364
627
  };
365
- }
628
+ },
629
+ sanitize: (spec) => sanitizeUiSpec(input, byName, spec)
366
630
  };
367
631
  }
368
632
  const tone = z.enum([
@@ -397,8 +661,38 @@ const DATA = [
397
661
  const INPUT = [
398
662
  "Form",
399
663
  "Field",
664
+ "FieldArray",
400
665
  "Button"
401
666
  ];
667
+ /**
668
+ * `visibleWhen` 的结构化条件(设计 23 §1.2)。用 `z.lazy` 自引用;
669
+ * 语义深度上限不在 zod 里卡,由 `evaluateFieldCondition` 按 `MAX_CONDITION_DEPTH` 降级处理。
670
+ */
671
+ const fieldCondition = z.lazy(() => z.union([
672
+ z.object({
673
+ field: z.string(),
674
+ equals: z.union([
675
+ z.string(),
676
+ z.number(),
677
+ z.boolean()
678
+ ])
679
+ }),
680
+ z.object({
681
+ field: z.string(),
682
+ in: z.array(z.union([z.string(), z.number()]))
683
+ }),
684
+ z.object({
685
+ field: z.string(),
686
+ notEmpty: z.literal(true)
687
+ }),
688
+ z.object({ allOf: z.array(fieldCondition).min(1) }),
689
+ z.object({ anyOf: z.array(fieldCondition).min(1) })
690
+ ]));
691
+ /** 选项既可以写死,也可以声明成异步取值(取值通道属分册 24) */
692
+ const fieldOptions = z.array(z.object({
693
+ label: z.string(),
694
+ value: z.union([z.string(), z.number()])
695
+ }));
402
696
  /** Tabs 的面板与 Grid 的格子必须自成容器,否则「每容器至多一个表单」就没有落脚点 */
403
697
  const PANEL = ["Card", "Stack"];
404
698
  /**
@@ -634,13 +928,16 @@ const uiCatalog = defineUiCatalog({
634
928
  group: "data",
635
929
  description: "Tabular data with explicit columns. Prefer it over Markdown tables.",
636
930
  props: z.object({
931
+ title: z.string().optional(),
637
932
  columns: z.array(z.string()).min(1),
638
933
  rows: z.array(z.array(z.union([
639
934
  z.string(),
640
935
  z.number(),
641
936
  z.boolean(),
642
937
  z.null()
643
- ])))
938
+ ]))),
939
+ /** 列宽权重,长度需与 columns 一致;不一致时整项忽略而不拒绝渲染 */
940
+ columnWidths: z.array(z.number().positive()).optional()
644
941
  }),
645
942
  example: {
646
943
  component: "Table",
@@ -738,7 +1035,7 @@ const uiCatalog = defineUiCatalog({
738
1035
  submitLabel: z.string().default("Submit"),
739
1036
  cancelLabel: z.string().optional()
740
1037
  }),
741
- children: ["Field"],
1038
+ children: ["Field", "FieldArray"],
742
1039
  singletonPerContainer: true,
743
1040
  constraints: ["At most one Form per container", "Every Field name must be unique inside the Form"],
744
1041
  example: {
@@ -770,17 +1067,22 @@ const uiCatalog = defineUiCatalog({
770
1067
  "select",
771
1068
  "multi-select",
772
1069
  "toggle",
773
- "file"
1070
+ "file",
1071
+ "password"
774
1072
  ]),
775
1073
  required: z.boolean().optional(),
776
1074
  description: z.string().optional(),
777
1075
  defaultValue: z.unknown().optional(),
778
- options: z.array(z.object({
779
- label: z.string(),
780
- value: z.union([z.string(), z.number()])
781
- })).optional()
1076
+ options: fieldOptions.optional(),
1077
+ /** 声明后选项由宕主异步提供;与写死的 options 互斥 */
1078
+ optionsSource: z.string().optional(),
1079
+ visibleWhen: fieldCondition.optional()
782
1080
  }),
783
- constraints: ["select / multi-select must provide options"],
1081
+ constraints: [
1082
+ "select / multi-select must provide options or optionsSource",
1083
+ "password values are never persisted, never echoed and never enter the user profile",
1084
+ "visibleWhen may only reference fields declared in the same Form"
1085
+ ],
784
1086
  example: {
785
1087
  component: "Field",
786
1088
  props: {
@@ -791,6 +1093,37 @@ const uiCatalog = defineUiCatalog({
791
1093
  }
792
1094
  }
793
1095
  },
1096
+ {
1097
+ name: "FieldArray",
1098
+ group: "input",
1099
+ description: "A repeatable group of fields. Only valid inside a Form; children must be Field nodes.",
1100
+ props: z.object({
1101
+ name: z.string(),
1102
+ label: z.string(),
1103
+ minItems: z.number().int().min(0).optional(),
1104
+ maxItems: z.number().int().min(1).optional(),
1105
+ addLabel: z.string().optional(),
1106
+ removeLabel: z.string().optional()
1107
+ }),
1108
+ children: ["Field"],
1109
+ constraints: ["Only valid inside a Form", "Submitted as an array of objects keyed by the inner Field names"],
1110
+ example: {
1111
+ component: "FieldArray",
1112
+ props: {
1113
+ name: "recipients",
1114
+ label: "Recipients",
1115
+ minItems: 1
1116
+ },
1117
+ children: [{
1118
+ component: "Field",
1119
+ props: {
1120
+ name: "email",
1121
+ label: "Email",
1122
+ type: "text"
1123
+ }
1124
+ }]
1125
+ }
1126
+ },
794
1127
  {
795
1128
  name: "Button",
796
1129
  group: "input",
@@ -935,7 +1268,7 @@ function a2uiComponentShapes(catalog) {
935
1268
  childList: def.children !== void 0,
936
1269
  ...childrenDescription ? { childListDescription: childrenDescription } : {},
937
1270
  actionProps: keys.filter((key) => key === ACTION_PROP),
938
- valueProps: def.group === "input" && keys.includes(NAME_PROP) ? [A2UI_VALUE_PROP] : [],
1271
+ valueProps: def.group === "input" && def.children === void 0 && keys.includes(NAME_PROP) ? [A2UI_VALUE_PROP] : [],
939
1272
  ...keys.includes(SUBMIT_LABEL_PROP) ? { submitLabelProp: SUBMIT_LABEL_PROP } : {},
940
1273
  ...keys.includes(CANCEL_LABEL_PROP) ? { cancelLabelProp: CANCEL_LABEL_PROP } : {}
941
1274
  };
@@ -985,101 +1318,7 @@ function buildA2uiCatalogDefinition(catalog) {
985
1318
 
986
1319
  //#endregion
987
1320
  //#region ../ui/dist/index.js
988
- /** 提交值按请求类型归形(WebFormBridge 与框架组件库共享单一来源) */
989
- function shapeInteractionValue(model, values) {
990
- switch (model.kind) {
991
- case "ask": return values["answer"];
992
- case "confirm": return values["confirmed"] === true;
993
- case "select": return values["selected"];
994
- case "authorize": return true;
995
- case "file-pick": return values["file"];
996
- case "form": return values;
997
- }
998
- }
999
- /** 五类 InteractionRequest → 统一中间模型(框架无关) */
1000
- function interactionToFormModel(request) {
1001
- switch (request.type) {
1002
- case "ask": return {
1003
- kind: "ask",
1004
- message: request.message,
1005
- controls: [{
1006
- name: "answer",
1007
- label: request.message,
1008
- control: "text",
1009
- required: true,
1010
- ...request.suggestion ? { suggestion: request.suggestion } : {}
1011
- }],
1012
- submitLabel: "Submit",
1013
- cancelLabel: "Cancel"
1014
- };
1015
- case "confirm": return {
1016
- kind: "confirm",
1017
- message: request.message,
1018
- controls: [{
1019
- name: "confirmed",
1020
- label: request.message,
1021
- control: "boolean",
1022
- defaultValue: request.defaultValue ?? true
1023
- }],
1024
- submitLabel: "Confirm",
1025
- cancelLabel: "Cancel"
1026
- };
1027
- case "form": return {
1028
- kind: "form",
1029
- ...request.title ? { title: request.title } : {},
1030
- controls: request.fields.map((f) => ({
1031
- name: f.name,
1032
- label: f.label,
1033
- control: f.type,
1034
- ...f.required ? { required: true } : {},
1035
- ...f.description ? { description: f.description } : {},
1036
- ...f.defaultValue !== void 0 ? { defaultValue: f.defaultValue } : {},
1037
- ...f.suggestion ? { suggestion: f.suggestion } : {},
1038
- ...f.options ? { options: f.options } : {}
1039
- })),
1040
- submitLabel: "Submit",
1041
- cancelLabel: "Cancel"
1042
- };
1043
- case "select": return {
1044
- kind: "select",
1045
- message: request.message,
1046
- controls: [{
1047
- name: "selected",
1048
- label: request.message,
1049
- control: "select",
1050
- required: true,
1051
- options: request.options,
1052
- ...request.suggestion ? { suggestion: request.suggestion } : {}
1053
- }],
1054
- submitLabel: "Select",
1055
- cancelLabel: "Cancel"
1056
- };
1057
- case "authorize": return {
1058
- kind: "authorize",
1059
- title: "Authorization required",
1060
- message: request.message,
1061
- controls: [],
1062
- submitLabel: "Allow",
1063
- cancelLabel: "Deny"
1064
- };
1065
- case "file-pick": return {
1066
- kind: "file-pick",
1067
- title: "File requested",
1068
- message: request.message,
1069
- controls: [{
1070
- name: "file",
1071
- label: request.field ?? "file",
1072
- control: "file",
1073
- required: true,
1074
- ...request.accept ? { accept: request.accept } : {},
1075
- ...request.multiple ? { multiple: true } : {}
1076
- }],
1077
- submitLabel: "Choose file",
1078
- cancelLabel: "Decline"
1079
- };
1080
- }
1081
- }
1082
- const isEmpty = (v) => v === void 0 || v === "";
1321
+ const isEmpty$1 = (v) => v === void 0 || v === "";
1083
1322
  /**
1084
1323
  * 控件名 → 属性选择器:优先 CSS.escape(标识符形式,免引号转义);
1085
1324
  * 无 CSS.escape 的环境退化为引号包裹 + 转义反斜杠/双引号(防选择器注入崩溃)
@@ -1118,7 +1357,7 @@ function collectValues(controls, container) {
1118
1357
  value = raw === "" ? void 0 : raw;
1119
1358
  }
1120
1359
  values[control.name] = value;
1121
- if (control.required && isEmpty(value)) missingRequired.push(control.name);
1360
+ if (control.required && isEmpty$1(value)) missingRequired.push(control.name);
1122
1361
  }
1123
1362
  return {
1124
1363
  values,
@@ -1489,17 +1728,21 @@ var WebFormBridge = class {
1489
1728
  #doc;
1490
1729
  #styles;
1491
1730
  #progressEl;
1731
+ #texts;
1732
+ #interactionTexts;
1492
1733
  /** 等待中的 request 取消句柄(id → 触发 cancelled 并卸载 DOM) */
1493
1734
  #pending = /* @__PURE__ */ new Map();
1494
1735
  constructor(options) {
1495
1736
  this.#mount = options.mount;
1496
1737
  this.#doc = options.document ?? options.mount.ownerDocument;
1497
1738
  this.#styles = options.styles ?? true;
1739
+ this.#interactionTexts = options.texts;
1740
+ this.#texts = resolveSurfaceFormTexts(options.texts);
1498
1741
  }
1499
1742
  request(input) {
1500
1743
  if (this.#styles) ensureStyles(this.#doc);
1501
1744
  this.#clearProgress();
1502
- const model = interactionToFormModel(input);
1745
+ const model = interactionToFormModel(input, this.#interactionTexts);
1503
1746
  const { form, cleanup } = this.#renderForm(model);
1504
1747
  return new Promise((resolve) => {
1505
1748
  const cancel = () => {
@@ -1655,7 +1898,7 @@ var WebFormBridge = class {
1655
1898
  error.className = "webskill-form__error";
1656
1899
  error.id = errorId;
1657
1900
  error.setAttribute("role", "alert");
1658
- error.textContent = "This field is required";
1901
+ error.textContent = this.#texts.required;
1659
1902
  wrapper.appendChild(error);
1660
1903
  return wrapper;
1661
1904
  }
@@ -1758,6 +2001,142 @@ function collectScopedValues(values, scope) {
1758
2001
  }
1759
2002
  return out;
1760
2003
  }
2004
+ /**
2005
+ * 条件树的语义深度上限。
2006
+ * `MAX_JSON_DEPTH` 管的是 props 的 JSON 深度,管不到这里——超它之前就能先把栈打爆。
2007
+ */
2008
+ const MAX_CONDITION_DEPTH = 8;
2009
+ const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2010
+ const isEmpty = (value) => value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
2011
+ function invalid(path, detail) {
2012
+ return {
2013
+ visible: true,
2014
+ degraded: {
2015
+ path,
2016
+ kind: "condition-invalid",
2017
+ detail
2018
+ }
2019
+ };
2020
+ }
2021
+ function evaluate(condition, context, depth) {
2022
+ if (depth > 8) return invalid(context.path, `Condition nesting exceeds 8 levels.`);
2023
+ if (!isRecord$1(condition)) return invalid(context.path, "Condition must be an object.");
2024
+ if (Array.isArray(condition["allOf"]) || Array.isArray(condition["anyOf"])) {
2025
+ const isAll = Array.isArray(condition["allOf"]);
2026
+ const branches = isAll ? condition["allOf"] : condition["anyOf"];
2027
+ if (branches.length === 0) return invalid(context.path, `"${isAll ? "allOf" : "anyOf"}" must not be empty.`);
2028
+ let visible = isAll;
2029
+ for (const branch of branches) {
2030
+ const result = evaluate(branch, context, depth + 1);
2031
+ if (result.degraded) return result;
2032
+ visible = isAll ? visible && result.visible : visible || result.visible;
2033
+ }
2034
+ return { visible };
2035
+ }
2036
+ const field = condition["field"];
2037
+ if (typeof field !== "string" || field === "") return invalid(context.path, "Condition needs a \"field\" name.");
2038
+ const value = context.resolve(field);
2039
+ if ("equals" in condition) {
2040
+ const expected = condition["equals"];
2041
+ if (typeof expected !== "string" && typeof expected !== "number" && typeof expected !== "boolean") return invalid(context.path, "\"equals\" must be a string, number or boolean.");
2042
+ return { visible: value === expected };
2043
+ }
2044
+ if ("in" in condition) {
2045
+ const options = condition["in"];
2046
+ if (!Array.isArray(options)) return invalid(context.path, "\"in\" must be an array.");
2047
+ return { visible: options.some((option) => option === value) };
2048
+ }
2049
+ if ("notEmpty" in condition) {
2050
+ if (condition["notEmpty"] !== true) return invalid(context.path, "\"notEmpty\" must be true.");
2051
+ return { visible: !isEmpty(value) };
2052
+ }
2053
+ return invalid(context.path, "Condition has no recognised operator (equals / in / notEmpty / allOf / anyOf).");
2054
+ }
2055
+ /**
2056
+ * 全仓唯一的条件求值实现(AC-23.4)。纯函数、无 React 依赖——
2057
+ * `useSurfaceForm` 在 ui-react 且是 hook,ui-vue 用不了它。
2058
+ */
2059
+ function evaluateFieldCondition(condition, values, options = {}) {
2060
+ const resolveField = options.resolveField;
2061
+ return evaluate(condition, {
2062
+ values,
2063
+ resolve: (field) => values[resolveField ? resolveField(field) : field],
2064
+ path: options.path ?? "visibleWhen"
2065
+ }, 1);
2066
+ }
2067
+ /**
2068
+ * 生成式表格的列模型(分册 15-03 定义 `priority`,分册 25 在同一结构上追加 `weight`)。
2069
+ *
2070
+ * 四个渲染档共用这一份:native / json-render / OpenUI 走 TS,a2ui 是 Lit,
2071
+ * 后者读不到 TS 常量,所以最小列宽以 **CSS 变量**交付,两侧引用同一个名字。
2072
+ */
2073
+ /** 最小列宽的分档取值。`desktop` 沿用历史值,改它会动所有既有表格的视觉基线 */
2074
+ const SPEC_TABLE_MIN_COLUMN_WIDTH = {
2075
+ desktop: "8rem",
2076
+ mobile: "5rem"
2077
+ };
2078
+ /** CSS 侧的引用名:`surfaces.css` 与 a2ui 的 Lit 样式都只写这个变量,不写字面量 */
2079
+ const SPEC_TABLE_MIN_COLUMN_VAR = "--webskill-table-min-col";
2080
+ /**
2081
+ * 校验并归一化 `columnWidths`。
2082
+ *
2083
+ * **整项忽略**是刻意的:部分采纳(例如只丢掉那个负数)会让模型拿到一个
2084
+ * 「看起来生效了」的结果,更难发现自己写错了(FR-25.3)。
2085
+ */
2086
+ function normalizeColumnWidths(raw, columnCount) {
2087
+ const equal = () => Array.from({ length: columnCount }, () => 1);
2088
+ if (raw === void 0) return { weights: equal() };
2089
+ if (raw.length !== columnCount) return {
2090
+ weights: equal(),
2091
+ rejected: "length-mismatch"
2092
+ };
2093
+ if (raw.some((value) => typeof value !== "number" || !Number.isFinite(value))) return {
2094
+ weights: equal(),
2095
+ rejected: "non-finite"
2096
+ };
2097
+ if (raw.some((value) => value < 0)) return {
2098
+ weights: equal(),
2099
+ rejected: "negative"
2100
+ };
2101
+ if (raw.every((value) => value === 0)) return {
2102
+ weights: equal(),
2103
+ rejected: "all-zero"
2104
+ };
2105
+ return { weights: [...raw] };
2106
+ }
2107
+ /**
2108
+ * 按权重分配列宽,且每列不低于 `minWidth`。
2109
+ *
2110
+ * 朴素实现(按权重算一遍、低于下限的抬到下限)会让总宽超出容器,
2111
+ * 白白产生本可避免的横滚。这里迭代到不动点:每轮把触底的列钉住,
2112
+ * 剩余空间在剩余列间按权重重新分配(FR-25.2)。
2113
+ *
2114
+ * 容器本身放不下 `columnCount * minWidth` 时无解——此时全部取下限并返回,
2115
+ * 由调用方决定横滚(AC-25.4)。
2116
+ */
2117
+ function resolveColumnWidths(weights, available, minWidth) {
2118
+ const count = weights.length;
2119
+ if (count === 0) return [];
2120
+ if (available <= count * minWidth) return Array.from({ length: count }, () => minWidth);
2121
+ const result = Array.from({ length: count }, () => 0);
2122
+ const pinned = new Array(count).fill(false);
2123
+ for (let round = 0; round <= count; round += 1) {
2124
+ const freeIndexes = result.map((_, index) => index).filter((index) => !pinned[index]);
2125
+ const remaining = available - result.reduce((sum, value, index) => pinned[index] ? sum + value : sum, 0);
2126
+ const weightTotal = freeIndexes.reduce((sum, index) => sum + (weights[index] ?? 0), 0);
2127
+ let changed = false;
2128
+ for (const index of freeIndexes) {
2129
+ const share = weightTotal > 0 ? remaining * (weights[index] ?? 0) / weightTotal : remaining / freeIndexes.length;
2130
+ if (share < minWidth) {
2131
+ result[index] = minWidth;
2132
+ pinned[index] = true;
2133
+ changed = true;
2134
+ } else result[index] = share;
2135
+ }
2136
+ if (!changed) break;
2137
+ }
2138
+ return result;
2139
+ }
1761
2140
  /** 五类场景预设(FR-6.4)。@experimental */
1762
2141
  const UI_PRESETS = [
1763
2142
  {
@@ -2044,14 +2423,15 @@ function toJsonRenderSpec(spec) {
2044
2423
  elements
2045
2424
  };
2046
2425
  }
2047
- /** ControlModel 的控件词汇 → catalog `Field.type`(catalog 的 8 种是超集) */
2426
+ /** ControlModel 的控件词汇 → catalog `Field.type`(catalog 的 9 种是超集) */
2048
2427
  const FIELD_TYPE = {
2049
2428
  text: "text",
2050
2429
  number: "number",
2051
2430
  boolean: "toggle",
2052
2431
  select: "select",
2053
2432
  textarea: "textarea",
2054
- file: "file"
2433
+ file: "file",
2434
+ password: "password"
2055
2435
  };
2056
2436
  function toOptions(options) {
2057
2437
  return (options ?? []).map((option) => ({
@@ -2491,7 +2871,7 @@ function fromA2uiSpecAction(event) {
2491
2871
  */
2492
2872
  async function loadWebSkillLitCatalog() {
2493
2873
  try {
2494
- const { webskillLitCatalog } = await import("./webskillLitCatalog-_mugzRHx-B_54vxum.js");
2874
+ const { webskillLitCatalog } = await import("./webskillLitCatalog-DwTwSBFt-DiXXpNZA.js");
2495
2875
  return webskillLitCatalog();
2496
2876
  } catch (cause) {
2497
2877
  throw new WebSkillError("UI_UNAVAILABLE", "The WebSkill A2UI catalog is unavailable; install @a2ui/lit, @a2ui/web_core and lit to render catalog surfaces with A2UI", cause);
@@ -2516,4 +2896,4 @@ async function loadOpenUiPeers() {
2516
2896
  }
2517
2897
 
2518
2898
  //#endregion
2519
- export { defineUiCatalog as $, loadOpenUiPeers as A, toOpenUiSpecLang as B, ensureStyles as C, fromVercelToolResult as D, fromUiSurfaceActionDispatch as E, renderRenderResult as F, A2UI_COMMON_TYPES as G, toVercelToolInvocation as H, shapeInteractionValue as I, WEBSKILL_A2UI_CATALOG_ID as J, UI_CATALOG_GROUPS as K, toA2uiSpecMessages as L, qualifyFieldName as M, renderBlocks as N, interactionToFormModel as O, renderMiniChart as P, chartSpecFromProps as Q, toA2uiSurfaceAction as R, createUiCatalogToolSource as S, fromA2uiSurfaceAction as T, uiPreset as U, toUiSurfaceActionDispatch as V, A2UI_CHILDREN_PROP as W, a2uiComponentShapes as X, a2uiComponentSchema as Y, buildA2uiCatalogDefinition as Z, chartToTable as _, A2UI_VERSION as a, collectSpecActions as b, RENDER_UI_TOOL as c, VERCEL_INTERACTION_TOOL_NAME as d, mountEchart as et, VercelUiBridge as f, applySuggestion as g, WebFormBridge as h, A2UI_SURFACE_ACTION as i, loadWebSkillLitCatalog as j, interactionToUiSpec as k, UI_PRESETS as l, WEBSKILL_SURFACE_ACTION as m, A2UI_SPEC_ACTION as n, uiCatalog as nt, CHART_PALETTE as o, WEBSKILL_STYLES_CSS as p, UI_CATALOG_PROMPT_BUDGET_BYTES as q, A2UI_SPEC_FORM_PATH as r, DESCRIBE_UI_PRESET_TOOL as s, A2UI_BASIC_CATALOG_ID as t, renderMiniMarkdown as tt, UI_PRESET_NAMES as u, collectFormScopes as v, fromA2uiSpecAction as w, collectValues as x, collectScopedValues as y, toJsonRenderSpec as z };
2899
+ export { UI_CATALOG_PROMPT_BUDGET_BYTES as $, fromUiSurfaceActionDispatch as A, resolveColumnWidths as B, collectSpecActions as C, evaluateFieldCondition as D, ensureStyles as E, normalizeColumnWidths as F, toUiSurfaceActionDispatch as G, toA2uiSurfaceAction as H, qualifyFieldName as I, A2UI_CHILDREN_PROP as J, toVercelToolInvocation as K, renderBlocks as L, interactionToUiSpec as M, loadOpenUiPeers as N, fromA2uiSpecAction as O, loadWebSkillLitCatalog as P, UI_CATALOG_GROUPS as Q, renderMiniChart as R, collectScopedValues as S, createUiCatalogToolSource as T, toJsonRenderSpec as U, toA2uiSpecMessages as V, toOpenUiSpecLang as W, DEFAULT_INTERACTION_TEXTS as X, A2UI_COMMON_TYPES as Y, DEFAULT_SURFACE_FORM_TEXTS as Z, WEBSKILL_SURFACE_ACTION as _, A2UI_VERSION as a, defineUiCatalog as at, chartToTable as b, MAX_CONDITION_DEPTH as c, renderMiniMarkdown as ct, SPEC_TABLE_MIN_COLUMN_WIDTH as d, shapeInteractionValue as dt, WEBSKILL_A2UI_CATALOG_ID as et, UI_PRESETS as f, uiCatalog as ft, WEBSKILL_STYLES_CSS as g, VercelUiBridge as h, A2UI_SURFACE_ACTION as i, chartSpecFromProps as it, fromVercelToolResult as j, fromA2uiSurfaceAction as k, RENDER_UI_TOOL as l, resolveInteractionTexts as lt, VERCEL_INTERACTION_TOOL_NAME as m, A2UI_SPEC_ACTION as n, a2uiComponentShapes as nt, CHART_PALETTE as o, interactionToFormModel as ot, UI_PRESET_NAMES as p, uiPreset as q, A2UI_SPEC_FORM_PATH as r, buildA2uiCatalogDefinition as rt, DESCRIBE_UI_PRESET_TOOL as s, mountEchart as st, A2UI_BASIC_CATALOG_ID as t, a2uiComponentSchema as tt, SPEC_TABLE_MIN_COLUMN_VAR as u, resolveSurfaceFormTexts as ut, WebFormBridge as v, collectValues as w, collectFormScopes as x, applySuggestion as y, renderRenderResult as z };