@webskill/sdk 0.20.0 → 0.22.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 (139) hide show
  1. package/dist/abnfDiagram-O67JEVCF-DtYFjfqO.js +109 -0
  2. package/dist/agent.d.ts +3 -3
  3. package/dist/agent.js +1171 -73
  4. package/dist/{approval-Bbh_Apwg.js → approval-DWQlDPbY.js} +527 -29
  5. package/dist/arc-8U_6NQWm.js +130 -0
  6. package/dist/architecture-7GRP2DOG-RTw1wpAq.js +4 -0
  7. package/dist/architectureDiagram-NJMV4G6O-XmmXrJAK.js +7473 -0
  8. package/dist/array-DQfpQNY8.js +8 -0
  9. package/dist/blockDiagram-BEXU5L5S-D4yjnCD1.js +2582 -0
  10. package/dist/browser.d.ts +1129 -48
  11. package/dist/browser.js +3562 -278
  12. package/dist/c4Diagram-YGBWAQC7-BLmA_JSd.js +3668 -0
  13. package/dist/channel-CZxxLrG7.js +9 -0
  14. package/dist/chunk-2Q5K7J3B-Ck03gkFR.js +21 -0
  15. package/dist/chunk-3FUC2YCW-tr6jmbQf.js +530 -0
  16. package/dist/chunk-5DYCD2WN-Bt97YGTd.js +58 -0
  17. package/dist/chunk-5VM5RSS4-BBxa48HL.js +21 -0
  18. package/dist/chunk-6AEJRKK7-JXK89I-O.js +14 -0
  19. package/dist/chunk-742MDFTN-CPmgCGwC.js +466 -0
  20. package/dist/chunk-7INBJB4K-C9jq9o66.js +6552 -0
  21. package/dist/chunk-7M6MHVWA-BrKCWcR1.js +4894 -0
  22. package/dist/chunk-7PRAP22T-BHED6GVs.js +95 -0
  23. package/dist/chunk-DUW6YSOI-DoZj78PO.js +19 -0
  24. package/dist/chunk-FOHPRMQF-Dkbp819z.js +27189 -0
  25. package/dist/chunk-GNY47TPC-Bpvq1T49.js +87 -0
  26. package/dist/chunk-GTNCS2PH-CLmh4I2c.js +686 -0
  27. package/dist/chunk-GWA4HPMP-B6vN3fkL.js +95 -0
  28. package/dist/chunk-J5ZVWO5B-Bi_Pcobw.js +26 -0
  29. package/dist/chunk-JWPE2WC7-D1rC79GS.js +12 -0
  30. package/dist/chunk-LNGE3PJU-D5OIoe3Z.js +2582 -0
  31. package/dist/chunk-MBY4JIJT-C34L7a4f.js +2276 -0
  32. package/dist/chunk-NETBCI7D-Cg84k8N8.js +321 -0
  33. package/dist/chunk-O7XYJQB3-B6K0KOSL.js +8846 -0
  34. package/dist/chunk-UA2S7LBM-DxHM29_K.js +612 -0
  35. package/dist/chunk-WEXAMYUT-Ba6jFWhB.js +33 -0
  36. package/dist/chunk-XXDRQBXY-DPbSy4kg.js +12 -0
  37. package/dist/chunk-Y2CYZVJY-U3QOMqrW.js +15 -0
  38. package/dist/chunk-Z7XXMR3K-oLg1h8F3.js +1038 -0
  39. package/dist/chunk-ZIGJFQKS-DqEFBuHR.js +2215 -0
  40. package/dist/classDiagram-v2-NBCMYWYE-BqnOWIUt.js +3845 -0
  41. package/dist/cose-bilkent-JH36ORCC-C0dTAHVK.js +4270 -0
  42. package/dist/cynefin-OW5HDTMX-B2QvgEQj.js +4 -0
  43. package/dist/cynefinDiagram-VND7K2PF-yAWc6Z3W.js +479 -0
  44. package/dist/cytoscape.esm-C64mRh9B.js +28325 -0
  45. package/dist/dagre-6A5THRUB-D0w2W27v.js +4816 -0
  46. package/dist/defaultLocale-B_EoOkSF.js +249 -0
  47. package/dist/diagram-22UHCM2B-DvgzhUzP.js +6115 -0
  48. package/dist/diagram-3UASUU5V-DWgUB-ef.js +795 -0
  49. package/dist/diagram-ATOU4E4O-P1olqwIs.js +553 -0
  50. package/dist/diagram-CDSNMT55-BUlZuLUe.js +402 -0
  51. package/dist/diagram-MLGK6HIB-6w5qmkGt.js +186 -0
  52. package/dist/diagram-MPIPVDR6-DEFiSo8o.js +289 -0
  53. package/dist/dist-UajCDt3n.js +92 -0
  54. package/dist/ebnfDiagram-ZINNZB2B-CI8liCfR.js +123 -0
  55. package/dist/elk-276RUBZZ-3SvW6OAK.js +90034 -0
  56. package/dist/erDiagram-OPXOYQCR-CPTbKhwP.js +2301 -0
  57. package/dist/{eventTypes-CwciaqCU.js → eventTypes-DcTQXSV1.js} +7 -1
  58. package/dist/eventmodeling-NTZA5JFV-BlyuqGp0.js +4 -0
  59. package/dist/flowDiagram-KWPJA3E3-CcUJ6HbV.js +18 -0
  60. package/dist/ganttDiagram-FUAMR5RP-31UjY0KK.js +3939 -0
  61. package/dist/geometry-wx5ZTYzi.js +715 -0
  62. package/dist/gitGraph-4MIJSDKK-ChdVlrk7.js +4 -0
  63. package/dist/gitGraphDiagram-X574FWY7-DKuxJymB.js +1153 -0
  64. package/dist/governance.d.ts +9 -3
  65. package/dist/governance.js +1 -1
  66. package/dist/graphlib-tk9gQYuN.js +4194 -0
  67. package/dist/{index-ad11wflI.d.ts → index-BiO9_XRk.d.ts} +2 -2
  68. package/dist/{index-DQ3-6GFu.d.ts → index-CjwdZQOS.d.ts} +945 -8
  69. package/dist/{index-BDyXe-a5.d.ts → index-YPd8ZSEa.d.ts} +413 -7
  70. package/dist/index.d.ts +4 -4
  71. package/dist/index.js +47 -41
  72. package/dist/info-A6RAGUB7-CxRAt9S9.js +4 -0
  73. package/dist/infoDiagram-VRGFBTTK-pRspGfjZ.js +25 -0
  74. package/dist/init-CMOIWdg-.js +16 -0
  75. package/dist/ishikawaDiagram-OU5B5YK6-DKerAPAg.js +975 -0
  76. package/dist/journeyDiagram-ZHPQQLJL-C4MK-YO5.js +1227 -0
  77. package/dist/kanban-definition-PNTS6WVX-BqsZpMXx.js +1244 -0
  78. package/dist/katex-BcRRvyh_.js +26465 -0
  79. package/dist/kind-Dc8x0HWz.js +110 -0
  80. package/dist/line-LgV8iVMV.js +48 -0
  81. package/dist/linear-BWKR0Tqd.js +393 -0
  82. package/dist/linkedDocument-CIuh_Yp0.js +416 -0
  83. package/dist/mcp.d.ts +2 -2
  84. package/dist/{memoryArtifactStore-D6dLeY55.js → memoryArtifactStore-Bs7glPqm.js} +14 -0
  85. package/dist/mermaid-parser.core-pYR5siSx.js +754 -0
  86. package/dist/mermaid.core-DMtwGCHy.js +1756 -0
  87. package/dist/mindmap-definition-NLK3R4M7-CXbD-myG.js +1312 -0
  88. package/dist/node.d.ts +8 -3
  89. package/dist/node.js +79 -4
  90. package/dist/{openUiLibrary-b0i7F_oZ.js → openUiLibrary-B7j3rLrm.js} +1 -1
  91. package/dist/{openUiSpecLang-CLXJEeRU.js → openUiSpecLang-BSYYnjay.js} +1 -1
  92. package/dist/ordinal-CxA6GHQS.js +84 -0
  93. package/dist/packet-AYTQ26CC-BOm08cxl.js +4 -0
  94. package/dist/path-C27Z2Bjv.js +106 -0
  95. package/dist/pegDiagram-GJSIUBJH-BLGJOewQ.js +115 -0
  96. package/dist/pie-WAS4IAKB-Dvg7glpk.js +4 -0
  97. package/dist/pieDiagram-5QR66LMP-laoaV_PB.js +298 -0
  98. package/dist/processSandboxEntry.js +5 -0
  99. package/dist/quadrantDiagram-O4NWA36T-Db_wFBkT.js +2236 -0
  100. package/dist/radar-RG4KPBEZ-BZDaTu06.js +4 -0
  101. package/dist/railroad-74A4TZTK-C-UhjkOy.js +4 -0
  102. package/dist/railroad-abnf-HS5TGJTU-dUKVA4OS.js +4 -0
  103. package/dist/railroad-ebnf-LZEXJU2U-DzQumk18.js +4 -0
  104. package/dist/railroad-peg-WCYAUIDC-B3vvTWed.js +4 -0
  105. package/dist/railroadDiagram-XR7U4H2S-BY0sTM4X.js +89 -0
  106. package/dist/requirementDiagram-PLB6GJNP-3rkQfEhs.js +2474 -0
  107. package/dist/rough.cjs-AmDd_NQk.js +1395 -0
  108. package/dist/sandboxWorkerEntry.js +5 -0
  109. package/dist/sankeyDiagram-IPEJSGJF-CGAJhyG2.js +3578 -0
  110. package/dist/sequenceDiagram-PO4LG4MO-9FJjFX06.js +5522 -0
  111. package/dist/sizeCapture-INFHLROL-D8PWxdOn.js +56 -0
  112. package/dist/{skillVersionStore-B24Jjjry-v2CCU0Xo.d.ts → skillVersionStore-B24Jjjry-BY1H7Krp.d.ts} +1 -1
  113. package/dist/src-NkhI3UMl.js +2653 -0
  114. package/dist/stateDiagram-v2-GCMORJYK-B0O3wc1-.js +2715 -0
  115. package/dist/swimlanes-2SLR337P-D4EOnrpu.js +6637 -0
  116. package/dist/swimlanesDiagram-TC7HE7FX-BUx29EJ9.js +30 -0
  117. package/dist/testing.d.ts +2 -1
  118. package/dist/testing.js +1 -1
  119. package/dist/timeline-definition-EJHVYXUP-CrgiiB4C.js +1513 -0
  120. package/dist/treeView-Q6P3EWNA-C1by-Gzc.js +4 -0
  121. package/dist/treemap-WGGIJYW6-mC2NZ6ad.js +4 -0
  122. package/dist/{types-Btpdd1y--BcxQ10Fa.d.ts → types-CpDRZ0rA-BM5FI9oN.d.ts} +111 -9
  123. package/dist/ui-react.d.ts +3 -3
  124. package/dist/ui-react.js +3718 -77
  125. package/dist/ui-vue.d.ts +1 -1
  126. package/dist/ui.d.ts +3 -3
  127. package/dist/ui.js +2 -2
  128. package/dist/untrustedText-BIaRvPZK.js +16 -0
  129. package/dist/usecaseDiagram-POWQR4AR-BUt_TQOY.js +14397 -0
  130. package/dist/vennDiagram-UO4OBE2U-CwXr0wjA.js +2808 -0
  131. package/dist/wardley-WFR3VGLG-CVSYjq_S.js +4 -0
  132. package/dist/wardleyDiagram-VNRHLVJA-Cpfd47dQ.js +874 -0
  133. package/dist/{webSkillApi-CsvA69qk.js → webSkillApi-Dy-Zjv0y.js} +1 -1
  134. package/dist/{webskillLitCatalog-W06npwej.js → webskillLitCatalog-D5BCiMCU.js} +1 -1
  135. package/dist/xychartDiagram-PMCCYNJV-e1TeVoc6.js +2704 -0
  136. package/package.json +6 -5
  137. package/dist/kind-DxgS8LM-.js +0 -77
  138. package/dist/linkedDocument-xNXF-z8n.js +0 -94
  139. package/dist/types-DOJI5YC3.js +0 -90
@@ -0,0 +1,715 @@
1
+ import { n as messageOf, t as WebSkillError } from "./errors-BDZNpC13.js";
2
+
3
+ //#region ../agent/src/frame.ts
4
+ /**
5
+ * 帧指称的展示形式(分册 18 FR-18.2)。
6
+ *
7
+ * 感知与操作两侧各自声明自己的 scope 类型(那是刻意的,见各自 types.ts),
8
+ * 但「一条帧路径写成给人看的字符串」只能有一份实现——
9
+ * 两处各写一遍,审计里的帧名和确认卡里的帧名就会慢慢对不上。
10
+ */
11
+ /**
12
+ * 帧路径的稳定展示串:`'self'` / `'#a'` / `'#a >>> #b'`。
13
+ *
14
+ * `>>>` **只是展示分隔符**:CSS 选择器里可以合法出现任意字符,
15
+ * 反向解析这个串取回路径是不成立的,实现层一律传原始形状(D-18-1)。
16
+ * @experimental
17
+ */
18
+ function frameLabel(frame) {
19
+ if (typeof frame === "string") return frame;
20
+ if (frame.length === 0) return "self";
21
+ return frame.join(" >>> ");
22
+ }
23
+ /** 把两种形状归一成逐层选择器数组;`'self'` 与空数组都归一成空数组 @experimental */
24
+ function frameSteps(frame) {
25
+ if (typeof frame === "string") return frame === "self" ? [] : [frame];
26
+ return frame;
27
+ }
28
+
29
+ //#endregion
30
+ //#region ../agent/src/perception/types.ts
31
+ /**
32
+ * 两种形状归一成帧列表;旧形状等价于一条 `frame:'self'`(FR-24.1)。
33
+ *
34
+ * **这是判别的单一来源。** 散在各处写 `'frames' in scope` 会让
35
+ * 「旧配置还等价吗」这个问题没有唯一答案。
36
+ * @experimental
37
+ */
38
+ function toFrameScopes(scope) {
39
+ if ("frames" in scope) return scope.frames;
40
+ return [{
41
+ frame: "self",
42
+ include: scope.include,
43
+ ...scope.exclude ? { exclude: scope.exclude } : {}
44
+ }];
45
+ }
46
+
47
+ //#endregion
48
+ //#region ../agent/src/pageAction/types.ts
49
+ /**
50
+ * 两种形状归一成帧列表;旧形状等价于一条 `frame:'self'`。
51
+ * 与感知侧的 `toFrameScopes` 一样,是本侧判别的**单一来源**。
52
+ * @experimental
53
+ */
54
+ function toActionFrameScopes(scope) {
55
+ if ("frames" in scope) return scope.frames;
56
+ return [{
57
+ frame: "self",
58
+ include: scope.include,
59
+ ...scope.exclude ? { exclude: scope.exclude } : {}
60
+ }];
61
+ }
62
+ /**
63
+ * 本版的操作集(FR-25.1 / 0.16.0 FR-13.6 / 0.18.0 FR-10.1 / 0.22.0 FR-17.1)。
64
+ * 导航只有 `back` 一个,且它**不接受地址**——可达面被浏览器历史封死(SC-1)。
65
+ *
66
+ * `drag` 只表达**起点与终点**,没有中途轨迹(0.22.0 FR-17.6 裁定,D64 就此撤销):
67
+ * 轨迹是实现细节不是意图,能画任意路径等于架空 AC-23.2,而且滑块验证码要的正是轨迹——
68
+ * 表达不了轨迹,这个动作就天然做不了人机验证绕过。
69
+ *
70
+ * 这些动作与既有几个走**同一条** policy 路径,
71
+ * 范围白名单、逐次确认、审计三条硬约束因此天然覆盖到它们——
72
+ * 前提是新动作不绕过 policy 直调执行器(AC-25.4 守这一点)。
73
+ * @experimental
74
+ */
75
+ const PAGE_ACTION_KINDS = [
76
+ "click",
77
+ "fill",
78
+ "submit",
79
+ "select",
80
+ "set",
81
+ "attach",
82
+ "scroll",
83
+ "hover",
84
+ "drag",
85
+ "back"
86
+ ];
87
+ /**
88
+ * `back` 的目标占位(0.16.0 分册 13 §4.1)。
89
+ *
90
+ * **模型不能表达它**:工具源在 `action === 'back'` 时拒绝任何 `ref` 入参。
91
+ * 它存在只为了让 `back` 不必改 `PageActionRequest.ref` 的类型——
92
+ * 改它会波及所有实现过 executor 的宓主。
93
+ * @experimental
94
+ */
95
+ const PAGE_DOCUMENT_REF = "@document";
96
+
97
+ //#endregion
98
+ //#region ../agent/src/document/budget.ts
99
+ /** @experimental */
100
+ const DOCUMENT_READ_BUDGET = {
101
+ textChars: 96e3,
102
+ visionImages: 8
103
+ };
104
+
105
+ //#endregion
106
+ //#region ../agent/src/document/format.ts
107
+ const PDF_MAGIC = [
108
+ 37,
109
+ 80,
110
+ 68,
111
+ 70,
112
+ 45
113
+ ];
114
+ const ZIP_MAGIC = [
115
+ 80,
116
+ 75,
117
+ 3,
118
+ 4
119
+ ];
120
+ function startsWith(bytes, magic) {
121
+ return magic.every((b, i) => bytes[i] === b);
122
+ }
123
+ /** zip 的本地文件头把条目名以明文 ASCII 存着,扫一遍即可,不必解压 */
124
+ function containsEntry(bytes, name) {
125
+ const needle = new TextEncoder().encode(name);
126
+ const find = (from, to) => {
127
+ outer: for (let i = from; i + needle.length <= to; i += 1) {
128
+ for (let j = 0; j < needle.length; j += 1) if (bytes[i + j] !== needle[j]) continue outer;
129
+ return true;
130
+ }
131
+ return false;
132
+ };
133
+ const WINDOW = 64 * 1024;
134
+ if (find(0, Math.min(bytes.length, WINDOW))) return true;
135
+ return find(Math.max(0, bytes.length - WINDOW * 2), bytes.length);
136
+ }
137
+ /**
138
+ * 格式以字节为准,文件名只作次要线索:改扩展名不该改变我们如何解析它。
139
+ * 认不出返回 `undefined`,由调用方归因。
140
+ * @experimental
141
+ */
142
+ function sniffDocumentFormat(bytes, name) {
143
+ if (startsWith(bytes, PDF_MAGIC)) return "pdf";
144
+ if (!startsWith(bytes, ZIP_MAGIC)) return void 0;
145
+ const lower = (name ?? "").toLowerCase();
146
+ if (lower.endsWith(".docx")) return "docx";
147
+ if (lower.endsWith(".xlsx")) return "xlsx";
148
+ if (lower.endsWith(".pptx")) return "pptx";
149
+ if (containsEntry(bytes, "word/document.xml")) return "docx";
150
+ if (containsEntry(bytes, "xl/workbook.xml")) return "xlsx";
151
+ if (containsEntry(bytes, "ppt/presentation.xml")) return "pptx";
152
+ }
153
+
154
+ //#endregion
155
+ //#region ../agent/src/document/pdfQuality.ts
156
+ /**
157
+ * 「这一页抽出来的是不是乱码」的判定(0.21.0 分册 22 · FR-22.2)。
158
+ *
159
+ * 判定必须是纯函数:它决定一页走文本还是走渲染,而渲染要烧一次多模态请求。
160
+ * 靠人眼看一遍不叫判据。
161
+ */
162
+ /** 坏字符占可打印字符的比例超过它即判文本层不可信 @experimental */
163
+ const PDF_GARBLED_RATIO = .2;
164
+ /** 短于它的页不参与比例判定:三五个字符里有一个私用区字符说明不了任何事 */
165
+ const MIN_SAMPLE = 16;
166
+ /**
167
+ * 坏字符有三类,来源各不相同:
168
+ * - 私用区 U+E000–U+F8FF:字体子集没有 `ToUnicode` 时,pdfjs 把 CID 原样落在这里
169
+ * - U+FFFD:解码失败的替换字符
170
+ * - C0/C1 控制字符:把二进制当文本读出来的残留
171
+ */
172
+ function isGarbledChar(code) {
173
+ if (code >= 57344 && code <= 63743) return true;
174
+ if (code === 65533) return true;
175
+ if (code < 32 && code !== 9 && code !== 10 && code !== 13) return true;
176
+ return code >= 127 && code <= 159;
177
+ }
178
+ /**
179
+ * 空白页一律判不可信:它要么是扫描页、要么什么都没有,
180
+ * 两种情况都该让模型自己看一眼,而不是把一页空气交上去。
181
+ * @experimental
182
+ */
183
+ function measurePdfTextQuality(text) {
184
+ let printable = 0;
185
+ let garbled = 0;
186
+ for (const ch of text) {
187
+ const code = ch.codePointAt(0) ?? 0;
188
+ if (/\s/u.test(ch)) continue;
189
+ printable += 1;
190
+ if (isGarbledChar(code)) garbled += 1;
191
+ }
192
+ if (printable === 0) return {
193
+ printable,
194
+ garbled,
195
+ trustworthy: false
196
+ };
197
+ if (printable < MIN_SAMPLE) return {
198
+ printable,
199
+ garbled,
200
+ trustworthy: garbled === 0
201
+ };
202
+ return {
203
+ printable,
204
+ garbled,
205
+ trustworthy: garbled / printable <= PDF_GARBLED_RATIO
206
+ };
207
+ }
208
+ /** `measurePdfTextQuality(text).trustworthy` 的简写 @experimental */
209
+ function isPdfTextTrustworthy(text) {
210
+ return measurePdfTextQuality(text).trustworthy;
211
+ }
212
+
213
+ //#endregion
214
+ //#region ../agent/src/document/deferral.ts
215
+ /** @experimental */
216
+ function takesImage(mode, role) {
217
+ if (mode === "vision") return true;
218
+ if (mode === "images") return role === "illustration";
219
+ return role === "content";
220
+ }
221
+ /** 本次没取、但回头取得到的图(进返回值的 `deferred`) @experimental */
222
+ function defersImage(mode, role) {
223
+ return mode === "auto" && role === "illustration";
224
+ }
225
+ /** 入参里的 `mode` 只认这三个字面量,其余一律当 `auto` @experimental */
226
+ function asReadMode(raw) {
227
+ return raw === "vision" || raw === "images" ? raw : "auto";
228
+ }
229
+
230
+ //#endregion
231
+ //#region ../agent/src/document/toolSource.ts
232
+ /** @experimental */
233
+ const READ_DOCUMENT_TOOL = "read_document";
234
+ /** 大图的门槛:低于它的是页眉 logo、印章、项目符号,投它们等于白烧一次多模态请求 @experimental */
235
+ const PDF_LARGE_IMAGE_RATIO = .15;
236
+ const DESCRIPTION = "Read a PDF, Word (.docx), Excel (.xlsx) or PowerPoint (.pptx) document the user attached or downloaded, one batch of units at a time. Pages that have no usable text layer (scans) are rendered as images, so their content is never lost. Pictures on pages whose text is readable are listed under \"deferred\" instead of being returned, because reading them costs an extra round trip; call again with mode=\"images\" to fetch those.";
237
+ const INPUT_SCHEMA = {
238
+ type: "object",
239
+ properties: {
240
+ id: {
241
+ type: "string",
242
+ description: "The attachment id, or the id from list_downloaded_files."
243
+ },
244
+ from: {
245
+ type: "object",
246
+ description: "Where to continue. Omit to start at the beginning; otherwise pass the \"next\" of the previous call.",
247
+ properties: {
248
+ page: {
249
+ type: "number",
250
+ description: "PDF only: 0-based page index."
251
+ },
252
+ block: {
253
+ type: "number",
254
+ description: "Word and PowerPoint: 0-based block index."
255
+ },
256
+ sheet: {
257
+ type: "number",
258
+ description: "Excel only: 0-based sheet index."
259
+ },
260
+ row: {
261
+ type: "number",
262
+ description: "Excel only: 0-based row index within the sheet."
263
+ },
264
+ slide: {
265
+ type: "number",
266
+ description: "PowerPoint only: 0-based slide index."
267
+ }
268
+ },
269
+ additionalProperties: false
270
+ },
271
+ mode: {
272
+ type: "string",
273
+ enum: [
274
+ "auto",
275
+ "images",
276
+ "vision"
277
+ ],
278
+ description: "\"auto\" (default) returns text plus the pictures that cannot be replaced by text, and lists the rest under \"deferred\". \"images\" returns only those deferred pictures and no text. \"vision\" (PDF only) renders every page as an image and ignores the text layer."
279
+ }
280
+ },
281
+ required: ["id"],
282
+ additionalProperties: false
283
+ };
284
+ const SYSTEM_PROMPT = "read_document reads an attached or downloaded PDF/Word/Excel/PowerPoint document in batches. It returns a \"next\" cursor whenever the document is not finished: keep calling read_document with that cursor until the result has no \"next\", otherwise you have only seen part of the document. Read the text first and answer from it; a result that carries \"deferred\" means those units hold pictures that were not read yet. Fetch them with mode=\"images\" only when the answer depends on what they show, and tell the user the pictures have not been looked at when you answer without them. Never guess an id, and never claim you read a document you only read part of.";
285
+ function toolError(e) {
286
+ return {
287
+ ok: false,
288
+ content: [],
289
+ error: {
290
+ code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
291
+ message: messageOf(e)
292
+ }
293
+ };
294
+ }
295
+ /** 包一层:直写 `signal?.aborted` 会被 TS 按第一次检查永久收窄成 false */
296
+ function aborted(signal) {
297
+ return signal !== void 0 && signal.aborted;
298
+ }
299
+ /**
300
+ * 逐单元读文档,读不出字的地方出图(0.21.0 分册 22)。
301
+ *
302
+ * 四个端口一个都没接时 `listToolSpecs()` 返回空——模型连这个工具的存在都看不到,
303
+ * 而不是看得到再被拒(AC-22.2)。
304
+ * @experimental
305
+ */
306
+ function createDocumentToolSource(options) {
307
+ const { files } = options;
308
+ const available = () => options.pdf !== void 0 || options.docx !== void 0 || options.xlsx !== void 0 || options.pptx !== void 0;
309
+ const budgetOf = () => options.budget?.() ?? DOCUMENT_READ_BUDGET;
310
+ return {
311
+ kind: "document-reader",
312
+ listToolSpecs: () => Promise.resolve(available() ? [{
313
+ name: READ_DOCUMENT_TOOL,
314
+ description: DESCRIPTION,
315
+ inputSchema: INPUT_SCHEMA
316
+ }] : []),
317
+ systemPrompt: () => Promise.resolve(available() ? SYSTEM_PROMPT : void 0),
318
+ canHandle: (name) => name === READ_DOCUMENT_TOOL,
319
+ argCaptureTrust: (name) => name === "read_document" ? { tier: "reviewed" } : void 0,
320
+ call: async (name, args, context) => {
321
+ try {
322
+ if (name !== "read_document") throw new WebSkillError("TOOL_NOT_FOUND", `Unknown tool ${name}.`);
323
+ return await read(args, context?.signal);
324
+ } catch (e) {
325
+ return toolError(e);
326
+ }
327
+ }
328
+ };
329
+ async function read(args, signal) {
330
+ const id = args["id"];
331
+ if (typeof id !== "string" || id === "") throw new WebSkillError("TOOL_UNSUPPORTED", "read_document requires the \"id\" of an attachment or download.");
332
+ const file = await files.read(id);
333
+ if (file === void 0) throw new WebSkillError("TOOL_UNSUPPORTED", `No readable document with id "${id}". It is either not an attachment of the current message, or a download id that has since expired — list the downloads again to get a fresh one.`);
334
+ const format = sniffDocumentFormat(file.bytes, file.name);
335
+ if (format === void 0) throw new WebSkillError("TOOL_UNSUPPORTED", `"${file.name}" is not a PDF, Word (.docx), Excel (.xlsx) or PowerPoint (.pptx) document, so read_document cannot read it.`);
336
+ const reader = readerFor(format);
337
+ if (reader === void 0) throw new WebSkillError("TOOL_UNSUPPORTED", `This environment cannot read ${format.toUpperCase()} documents.`);
338
+ const from = asCursor(args["from"]);
339
+ const mode = asReadMode(args["mode"]);
340
+ const budget = budgetOf();
341
+ const batch = format === "pdf" ? await readPdf(reader, file.bytes, from, mode, budget, signal) : format === "docx" ? await readDocx(reader, file.bytes, from, mode, budget, signal) : format === "pptx" ? await readPptx(reader, file.bytes, from, mode, budget, signal) : await readXlsx(reader, file.bytes, from, mode, budget, signal);
342
+ return {
343
+ ok: true,
344
+ content: describe(file.name, format, batch, aborted(signal))
345
+ };
346
+ }
347
+ function readerFor(format) {
348
+ if (format === "pdf") return options.pdf;
349
+ if (format === "docx") return options.docx;
350
+ if (format === "pptx") return options.pptx;
351
+ return options.xlsx;
352
+ }
353
+ }
354
+ function asCursor(raw) {
355
+ if (typeof raw !== "object" || raw === null) return {};
356
+ const out = {};
357
+ for (const key of [
358
+ "page",
359
+ "block",
360
+ "sheet",
361
+ "row",
362
+ "slide"
363
+ ]) {
364
+ const value = raw[key];
365
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0) out[key] = value;
366
+ }
367
+ return out;
368
+ }
369
+ /**
370
+ * 预算是「加上这一个还超不超」,不是「已经超了没有」:
371
+ * 后者会把一个超大单元整个吞下去再喊停,等于预算形同虚设。
372
+ * 但**至少读一个单元**,否则一个超预算的单元会让续读原地打转。
373
+ */
374
+ function wouldExceed(budget, taken, chars, images) {
375
+ if (taken === 0) return false;
376
+ return chars > budget.textChars || images > budget.visionImages;
377
+ }
378
+ async function readPdf(reader, bytes, from, mode, budget, signal) {
379
+ const handle = await reader.open(bytes);
380
+ try {
381
+ const start = from["page"] ?? 0;
382
+ const units = [];
383
+ const deferred = [];
384
+ let chars = 0;
385
+ let images = 0;
386
+ let page = start;
387
+ for (; page < handle.pageCount; page += 1) {
388
+ if (aborted(signal)) break;
389
+ const text = mode === "vision" ? "" : await handle.text(page);
390
+ const trustworthy = mode !== "vision" && isPdfTextTrustworthy(text);
391
+ const role = trustworthy ? "illustration" : "content";
392
+ const hasImage = role === "content" || await hasLargeImage(handle, page);
393
+ const needsImage = hasImage && takesImage(mode, role);
394
+ if (mode === "images" && !needsImage) continue;
395
+ const withText = trustworthy && mode !== "images";
396
+ const nextChars = chars + (withText ? text.length : 0);
397
+ const nextImages = images + (needsImage ? 1 : 0);
398
+ if (wouldExceed(budget, units.length, nextChars, nextImages)) break;
399
+ if (aborted(signal)) break;
400
+ const rendered = needsImage ? await handle.render(page) : void 0;
401
+ if (hasImage && defersImage(mode, role)) deferred.push({ page });
402
+ units.push({
403
+ unit: { page },
404
+ ...withText ? { text } : {},
405
+ ...rendered ? { image: rendered } : {}
406
+ });
407
+ chars = nextChars;
408
+ images = nextImages;
409
+ }
410
+ return {
411
+ units,
412
+ total: handle.pageCount,
413
+ done: page,
414
+ skippedImages: 0,
415
+ deferred,
416
+ ...page < handle.pageCount ? { next: { page } } : {}
417
+ };
418
+ } finally {
419
+ await handle.close();
420
+ }
421
+ }
422
+ async function hasLargeImage(handle, page) {
423
+ return (await handle.images(page)).some((image) => image.areaRatio >= PDF_LARGE_IMAGE_RATIO);
424
+ }
425
+ async function readDocx(reader, bytes, from, mode, budget, signal) {
426
+ const { blocks } = await reader.read(bytes);
427
+ const taken = takeBlocks(blocks, from["block"] ?? 0, mode, budget, signal, (index) => ({ block: index }));
428
+ return {
429
+ units: taken.units,
430
+ total: blocks.length,
431
+ done: taken.stopped,
432
+ skippedImages: taken.skippedImages,
433
+ deferred: taken.deferred,
434
+ ...taken.stopped < blocks.length ? { next: { block: taken.stopped } } : {}
435
+ };
436
+ }
437
+ async function readXlsx(reader, bytes, from, mode, budget, signal) {
438
+ const { sheets } = await reader.read(bytes);
439
+ const flat = [];
440
+ sheets.forEach((sheet, sheetIndex) => {
441
+ sheet.rows.forEach((block, rowIndex) => {
442
+ flat.push({
443
+ sheet: sheetIndex,
444
+ sheetName: sheet.name,
445
+ row: rowIndex,
446
+ block
447
+ });
448
+ });
449
+ });
450
+ const startSheet = from["sheet"] ?? 0;
451
+ const startRow = from["row"] ?? 0;
452
+ const found = flat.findIndex((entry) => entry.sheet > startSheet || entry.sheet === startSheet && entry.row >= startRow);
453
+ const start = found === -1 ? flat.length : found;
454
+ const taken = takeBlocks(flat.map((entry) => entry.block), start, mode, budget, signal, (index) => ({
455
+ sheet: flat[index].sheet,
456
+ sheetName: flat[index].sheetName,
457
+ row: flat[index].row
458
+ }));
459
+ const stop = flat[taken.stopped];
460
+ return {
461
+ units: taken.units,
462
+ total: flat.length,
463
+ done: taken.stopped,
464
+ skippedImages: taken.skippedImages,
465
+ deferred: taken.deferred,
466
+ ...stop ? { next: {
467
+ sheet: stop.sheet,
468
+ row: stop.row
469
+ } } : {}
470
+ };
471
+ }
472
+ /**
473
+ * pptx 摊平成「第几页第几块」的一条序列,与 xlsx 的表×行同构。
474
+ * 幻灯片真有页,所以坐标保留页号——模型报「第 7 页那张图」时对得上用户看到的东西。
475
+ */
476
+ async function readPptx(reader, bytes, from, mode, budget, signal) {
477
+ const { slides } = await reader.read(bytes);
478
+ const flat = [];
479
+ slides.forEach((slide, slideIndex) => {
480
+ slide.blocks.forEach((content, blockIndex) => {
481
+ flat.push({
482
+ slide: slideIndex,
483
+ block: blockIndex,
484
+ content
485
+ });
486
+ });
487
+ });
488
+ const startSlide = from["slide"] ?? 0;
489
+ const startBlock = from["block"] ?? 0;
490
+ const found = flat.findIndex((entry) => entry.slide > startSlide || entry.slide === startSlide && entry.block >= startBlock);
491
+ const start = found === -1 ? flat.length : found;
492
+ const taken = takeBlocks(flat.map((entry) => entry.content), start, mode, budget, signal, (index) => ({
493
+ slide: flat[index].slide,
494
+ block: flat[index].block
495
+ }));
496
+ const stop = flat[taken.stopped];
497
+ return {
498
+ units: taken.units,
499
+ total: flat.length,
500
+ done: taken.stopped,
501
+ skippedImages: taken.skippedImages,
502
+ deferred: taken.deferred,
503
+ ...stop ? { next: {
504
+ slide: stop.slide,
505
+ block: stop.block
506
+ } } : {}
507
+ };
508
+ }
509
+ function takeBlocks(blocks, start, mode, budget, signal, locate) {
510
+ const units = [];
511
+ const deferred = [];
512
+ let chars = 0;
513
+ let images = 0;
514
+ let skippedImages = 0;
515
+ let index = start;
516
+ for (; index < blocks.length; index += 1) {
517
+ if (aborted(signal)) break;
518
+ const block = blocks[index];
519
+ if (block.kind === "skipped") {
520
+ skippedImages += 1;
521
+ continue;
522
+ }
523
+ if (!(block.kind === "text" ? mode !== "images" : takesImage(mode, "illustration"))) {
524
+ if (block.kind === "image" && defersImage(mode, "illustration")) deferred.push(locate(index));
525
+ continue;
526
+ }
527
+ const nextChars = chars + (block.kind === "text" ? block.text.length : 0);
528
+ const nextImages = images + (block.kind === "image" ? 1 : 0);
529
+ if (wouldExceed(budget, units.length, nextChars, nextImages)) break;
530
+ units.push({
531
+ unit: locate(index),
532
+ ...block.kind === "text" ? { text: block.text } : {},
533
+ ...block.kind === "image" ? { image: {
534
+ mimeType: block.mimeType,
535
+ data: block.data
536
+ } } : {}
537
+ });
538
+ chars = nextChars;
539
+ images = nextImages;
540
+ }
541
+ return {
542
+ units,
543
+ stopped: index,
544
+ skippedImages,
545
+ deferred
546
+ };
547
+ }
548
+ /**
549
+ * 图只能走 user 消息,出处只能随同一结果里的 json 分片走——
550
+ * 与下载工具源同款(引擎把图转成 carried 时会丢掉 id 与文件名)。
551
+ */
552
+ function describe(name, format, batch, cancelled) {
553
+ const images = [];
554
+ const units = batch.units.map((entry) => {
555
+ const located = {
556
+ ...entry.unit,
557
+ ...entry.text === void 0 ? {} : { text: entry.text }
558
+ };
559
+ if (entry.image === void 0) return located;
560
+ const imageId = imageIdOf(entry.unit);
561
+ images.push({
562
+ type: "image",
563
+ id: imageId,
564
+ mimeType: entry.image.mimeType,
565
+ data: entry.image.data
566
+ });
567
+ return {
568
+ ...located,
569
+ imageId
570
+ };
571
+ });
572
+ const withText = batch.units.filter((entry) => entry.text !== void 0).length;
573
+ const withImage = images.length;
574
+ const remaining = Math.max(0, batch.total - batch.done);
575
+ const json = {
576
+ file: name,
577
+ format,
578
+ source: withImage === 0 ? "text" : withText === 0 ? "vision" : "mixed",
579
+ total: batch.total,
580
+ units,
581
+ remaining,
582
+ note: continuationNote(format, batch, remaining, cancelled)
583
+ };
584
+ if (batch.next !== void 0) {
585
+ json["next"] = batch.next;
586
+ json["truncated"] = true;
587
+ }
588
+ if (batch.skippedImages > 0) json["skipped"] = {
589
+ images: batch.skippedImages,
590
+ reason: "EMF/WMF images cannot be decoded in a browser and were left out."
591
+ };
592
+ if (batch.deferred.length > 0) json["deferred"] = {
593
+ images: batch.deferred.length,
594
+ units: batch.deferred,
595
+ note: `${batch.deferred.length} unit(s) above hold pictures that were not read, because their text is readable on its own. Answer from the text when you can. If the answer depends on what a picture shows, call read_document again with mode="images" and from = ${JSON.stringify(batch.deferred[0])}, and say so if you answer without looking at them.`
596
+ };
597
+ return [{
598
+ type: "json",
599
+ data: json
600
+ }, ...images];
601
+ }
602
+ function continuationNote(format, batch, remaining, cancelled) {
603
+ const noun = format === "pdf" ? "page" : format === "xlsx" ? "row" : "block";
604
+ if (batch.next === void 0) return `Reached the end of the document; all ${batch.total} ${noun}s have been read.`;
605
+ const cursor = JSON.stringify(batch.next);
606
+ return `${cancelled ? "Stopped because the user cancelled the run" : `${remaining} ${noun}(s) still unread`}. Call read_document again with from = ${cursor} to continue.`;
607
+ }
608
+ /** 由单元坐标推出,因此与 `units[].imageId` 天然对得上,不依赖批次内的序号 */
609
+ function imageIdOf(unit) {
610
+ if (typeof unit["page"] === "number") return `doc-p${unit["page"]}`;
611
+ if (typeof unit["block"] === "number") return `doc-b${unit["block"]}`;
612
+ return `doc-s${unit["sheet"]}r${unit["row"]}`;
613
+ }
614
+
615
+ //#endregion
616
+ //#region ../agent/src/template/geometry.ts
617
+ /**
618
+ * A1 地址与 OOXML 几何换算(0.22.0 分册 42 · 设计 43 §1.6)。
619
+ *
620
+ * 环境无关,`@webskill/agent` 里可直接单测——几何算错的后果是图溢出单元格,
621
+ * 而那只有人肉打开文件才看得见,所以这一层的覆盖要密。
622
+ */
623
+ /** 1 px 的 EMU 数(96 DPI) */
624
+ const EMU_PER_PIXEL = 9525;
625
+ /** 1 磅的 EMU 数 */
626
+ const EMU_PER_POINT = 12700;
627
+ /** 1 twip 的 EMU 数 */
628
+ const EMU_PER_TWIP = 635;
629
+ /** 默认字体的最大数字宽度,Excel 列宽换算基准 */
630
+ const MAX_DIGIT_WIDTH = 7;
631
+ const ADDRESS = /^([A-Z]+)([1-9][0-9]*)$/;
632
+ /** @throws WebSkillError 语法不合法 */
633
+ function parseCellAddress(address) {
634
+ const match = ADDRESS.exec(address.trim().toUpperCase());
635
+ if (match === null) throw new WebSkillError("TEMPLATE_ADDRESS_INVALID", `"${address}" is not a valid cell address. Use A1 notation such as B3.`);
636
+ let column = 0;
637
+ for (const letter of match[1] ?? "") column = column * 26 + (letter.charCodeAt(0) - 64);
638
+ return {
639
+ column: column - 1,
640
+ row: Number(match[2]) - 1
641
+ };
642
+ }
643
+ function formatCellAddress({ column, row }) {
644
+ let letters = "";
645
+ for (let rest = column + 1; rest > 0; rest = Math.floor((rest - 1) / 26)) letters = String.fromCharCode(65 + (rest - 1) % 26) + letters;
646
+ return `${letters}${row + 1}`;
647
+ }
648
+ /** @throws WebSkillError 语法不合法 */
649
+ function parseCellRange(range) {
650
+ const [from = "", to] = range.split(":");
651
+ if (to === void 0) {
652
+ const single = parseCellAddress(from);
653
+ return {
654
+ start: single,
655
+ end: single
656
+ };
657
+ }
658
+ return {
659
+ start: parseCellAddress(from),
660
+ end: parseCellAddress(to)
661
+ };
662
+ }
663
+ function isRangeTopLeft(range, address) {
664
+ return range.start.column === address.column && range.start.row === address.row;
665
+ }
666
+ function rangeContains(range, address) {
667
+ return address.column >= range.start.column && address.column <= range.end.column && address.row >= range.start.row && address.row <= range.end.row;
668
+ }
669
+ /** Excel 的列宽单位是字符数,换算成像素(设计 43 §1.6 实测 MDW=7) */
670
+ function columnWidthToPixels(width) {
671
+ return Math.round(width * 7 + 5);
672
+ }
673
+ /**
674
+ * 等比缩放进框:宽高同时约束、**取两个比例里的小者**。
675
+ *
676
+ * FR-42.11 与 FR-42.19 是同一条口径——xlsx 的合并区和 docx 声明了 `trHeight` 的
677
+ * 表格行都这么算。框的某一边为 0(未声明高度)时那一边不参与约束。
678
+ */
679
+ function fitWithin(natural, box) {
680
+ const widthRatio = box.width > 0 ? box.width / natural.width : Number.POSITIVE_INFINITY;
681
+ const heightRatio = box.height > 0 ? box.height / natural.height : Number.POSITIVE_INFINITY;
682
+ const ratio = Math.min(widthRatio, heightRatio, 1);
683
+ if (!Number.isFinite(ratio)) return natural;
684
+ return {
685
+ width: Math.floor(natural.width * ratio),
686
+ height: Math.floor(natural.height * ratio)
687
+ };
688
+ }
689
+ /** PNG / JPEG 的像素尺寸。读文件头,不解码整张图。 */
690
+ function readImageSize(bytes) {
691
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
692
+ if (bytes.length > 24 && view.getUint32(0) === 2303741511) return {
693
+ width: view.getUint32(16),
694
+ height: view.getUint32(20)
695
+ };
696
+ if (bytes.length > 4 && view.getUint16(0) === 65496) return readJpegSize(view, bytes.length);
697
+ }
698
+ function readJpegSize(view, length) {
699
+ let offset = 2;
700
+ while (offset + 9 < length) {
701
+ if (view.getUint8(offset) !== 255) {
702
+ offset += 1;
703
+ continue;
704
+ }
705
+ const marker = view.getUint8(offset + 1);
706
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
707
+ width: view.getUint16(offset + 7),
708
+ height: view.getUint16(offset + 5)
709
+ };
710
+ offset += 2 + view.getUint16(offset + 2);
711
+ }
712
+ }
713
+
714
+ //#endregion
715
+ export { DOCUMENT_READ_BUDGET as C, toFrameScopes as D, toActionFrameScopes as E, frameLabel as O, sniffDocumentFormat as S, PAGE_DOCUMENT_REF as T, defersImage as _, columnWidthToPixels as a, isPdfTextTrustworthy as b, isRangeTopLeft as c, rangeContains as d, readImageSize as f, asReadMode as g, createDocumentToolSource as h, MAX_DIGIT_WIDTH as i, frameSteps as k, parseCellAddress as l, READ_DOCUMENT_TOOL as m, EMU_PER_POINT as n, fitWithin as o, PDF_LARGE_IMAGE_RATIO as p, EMU_PER_TWIP as r, formatCellAddress as s, EMU_PER_PIXEL as t, parseCellRange as u, takesImage as v, PAGE_ACTION_KINDS as w, measurePdfTextQuality as x, PDF_GARBLED_RATIO as y };