@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
package/dist/agent.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { n as messageOf, t as WebSkillError } from "./errors-BDZNpC13.js";
2
2
  import { b as parseSkillMarkdown, y as isValidSkillName } from "./skill-CAJMsLod.js";
3
3
  import { t as assertRemoteUrlAllowed } from "./urlSafety-CiSuCJvX.js";
4
- import { c as formatAttachmentText, l as UNTRUSTED_LINE_LIMIT, o as XLSX_MIME, s as classifyAttachment, u as sanitizeUntrustedLine } from "./kind-DxgS8LM-.js";
5
- import { o as toBase64, s as DEFAULT_MAX_DATA_SOURCE_BYTES } from "./linkedDocument-xNXF-z8n.js";
6
- import { a as frameLabel, i as toFrameScopes, n as PAGE_DOCUMENT_REF, o as frameSteps, r as toActionFrameScopes, t as PAGE_ACTION_KINDS } from "./types-DOJI5YC3.js";
4
+ import { n as sanitizeUntrustedLine, t as UNTRUSTED_LINE_LIMIT } from "./untrustedText-BIaRvPZK.js";
5
+ import { d as extractedDocumentFormat, f as formatAttachmentText, u as classifyAttachment } from "./kind-Dc8x0HWz.js";
6
+ import { c as toBase64, d as SPREADSHEET_LIMITS, f as SPREADSHEET_MIME_TYPE, l as encodeSpreadsheet, m as DEFAULT_MAX_DATA_SOURCE_BYTES, u as SPREADSHEET_EXTENSION } from "./linkedDocument-CIuh_Yp0.js";
7
+ import { C as DOCUMENT_READ_BUDGET, D as toFrameScopes, E as toActionFrameScopes, O as frameLabel, S as sniffDocumentFormat, T as PAGE_DOCUMENT_REF, _ as defersImage, a as columnWidthToPixels, b as isPdfTextTrustworthy, c as isRangeTopLeft, d as rangeContains, f as readImageSize, g as asReadMode, h as createDocumentToolSource, i as MAX_DIGIT_WIDTH, k as frameSteps, l as parseCellAddress, m as READ_DOCUMENT_TOOL, n as EMU_PER_POINT, o as fitWithin, p as PDF_LARGE_IMAGE_RATIO, r as EMU_PER_TWIP, s as formatCellAddress, t as EMU_PER_PIXEL, u as parseCellRange, v as takesImage, w as PAGE_ACTION_KINDS, x as measurePdfTextQuality, y as PDF_GARBLED_RATIO } from "./geometry-wx5ZTYzi.js";
7
8
 
8
9
  //#region ../agent/src/todo/store.ts
9
10
  const STATUSES = [
@@ -129,7 +130,7 @@ const STATUS_ENUM = [
129
130
  "completed"
130
131
  ];
131
132
  const TODO_TOOL_DESCRIPTION = "Maintain the visible task list for a multi-step request. The rules are in the system prompt.";
132
- const INPUT_SCHEMA$4 = {
133
+ const INPUT_SCHEMA$6 = {
133
134
  type: "object",
134
135
  properties: {
135
136
  action: {
@@ -182,7 +183,7 @@ const INPUT_SCHEMA$4 = {
182
183
  required: ["action"],
183
184
  additionalProperties: false
184
185
  };
185
- function toolError$6(code, message) {
186
+ function toolError$8(code, message) {
186
187
  return {
187
188
  ok: false,
188
189
  content: [],
@@ -231,15 +232,15 @@ function createTodoToolSource(options = {}) {
231
232
  try {
232
233
  if (action === "create") {
233
234
  const items = parseItems(args["items"]);
234
- if (typeof items === "string") return Promise.resolve(toolError$6("VALIDATION_FAILED", items));
235
+ if (typeof items === "string") return Promise.resolve(toolError$8("VALIDATION_FAILED", items));
235
236
  const list = store.create(items);
236
237
  return Promise.resolve(ok(events, progressText(list.items)));
237
238
  }
238
239
  if (action === "update") {
239
240
  const id = args["id"];
240
241
  const status = args["status"];
241
- if (typeof id !== "string") return Promise.resolve(toolError$6("VALIDATION_FAILED", "The \"id\" argument is required for action \"update\""));
242
- if (status !== void 0 && typeof status !== "string") return Promise.resolve(toolError$6("VALIDATION_FAILED", "The \"status\" argument must be a string"));
242
+ if (typeof id !== "string") return Promise.resolve(toolError$8("VALIDATION_FAILED", "The \"id\" argument is required for action \"update\""));
243
+ if (status !== void 0 && typeof status !== "string") return Promise.resolve(toolError$8("VALIDATION_FAILED", "The \"status\" argument must be a string"));
243
244
  const title = args["title"];
244
245
  const list = store.update(id, {
245
246
  ...status !== void 0 ? { status } : {},
@@ -251,10 +252,10 @@ function createTodoToolSource(options = {}) {
251
252
  store.clear();
252
253
  return Promise.resolve(ok(events, progressText([])));
253
254
  }
254
- return Promise.resolve(toolError$6("VALIDATION_FAILED", "The \"action\" argument must be one of create, update, clear"));
255
+ return Promise.resolve(toolError$8("VALIDATION_FAILED", "The \"action\" argument must be one of create, update, clear"));
255
256
  } catch (e) {
256
257
  const code = e instanceof Error && "code" in e ? String(e.code) : "VALIDATION_FAILED";
257
- return Promise.resolve(toolError$6(code, e instanceof Error ? e.message : String(e)));
258
+ return Promise.resolve(toolError$8(code, e instanceof Error ? e.message : String(e)));
258
259
  } finally {
259
260
  unsubscribe();
260
261
  }
@@ -266,7 +267,7 @@ function createTodoToolSource(options = {}) {
266
267
  listToolSpecs: () => Promise.resolve([{
267
268
  name: MANAGE_TODO_TOOL,
268
269
  description: TODO_TOOL_DESCRIPTION,
269
- inputSchema: INPUT_SCHEMA$4
270
+ inputSchema: INPUT_SCHEMA$6
270
271
  }]),
271
272
  canHandle: (name) => name === MANAGE_TODO_TOOL,
272
273
  argCaptureTrust: (name) => name === "manage_todo" ? { tier: "reviewed" } : void 0,
@@ -499,7 +500,7 @@ const SKILL_GENERATION_SYSTEM_PROMPT = [
499
500
  //#region ../agent/src/skillGeneration/toolSource.ts
500
501
  const GENERATE_SKILL_TOOL = "generate_skill";
501
502
  const TOOL_DESCRIPTION = "Turn the procedure just demonstrated in this conversation into a skill candidate. The user must confirm it, and a reviewer must approve it before anyone can use it.";
502
- const INPUT_SCHEMA$3 = {
503
+ const INPUT_SCHEMA$5 = {
503
504
  type: "object",
504
505
  properties: {
505
506
  name: {
@@ -557,7 +558,7 @@ const INPUT_SCHEMA$3 = {
557
558
  ],
558
559
  additionalProperties: false
559
560
  };
560
- function toolError$5(code, message) {
561
+ function toolError$7(code, message) {
561
562
  return {
562
563
  ok: false,
563
564
  content: [],
@@ -626,7 +627,7 @@ function createSkillGenerationToolSource(options) {
626
627
  const generator = new SkillGenerator(options);
627
628
  const call = async (_name, args) => {
628
629
  const draft = readDraft(args);
629
- if (typeof draft === "string") return toolError$5("SKILL_GENERATION_VALIDATION_FAILED", draft);
630
+ if (typeof draft === "string") return toolError$7("SKILL_GENERATION_VALIDATION_FAILED", draft);
630
631
  try {
631
632
  const outcome = await generator.generate(draft);
632
633
  if (outcome.status === "declined") return {
@@ -650,7 +651,7 @@ function createSkillGenerationToolSource(options) {
650
651
  }]
651
652
  };
652
653
  } catch (e) {
653
- return toolError$5(e instanceof Error && "code" in e ? String(e.code) : "SKILL_GENERATION_VALIDATION_FAILED", e instanceof Error ? e.message : String(e));
654
+ return toolError$7(e instanceof Error && "code" in e ? String(e.code) : "SKILL_GENERATION_VALIDATION_FAILED", e instanceof Error ? e.message : String(e));
654
655
  }
655
656
  };
656
657
  return {
@@ -659,7 +660,7 @@ function createSkillGenerationToolSource(options) {
659
660
  listToolSpecs: () => Promise.resolve([{
660
661
  name: GENERATE_SKILL_TOOL,
661
662
  description: TOOL_DESCRIPTION,
662
- inputSchema: INPUT_SCHEMA$3
663
+ inputSchema: INPUT_SCHEMA$5
663
664
  }]),
664
665
  canHandle: (name) => name === GENERATE_SKILL_TOOL,
665
666
  argCaptureTrust: (name) => name === "generate_skill" ? { tier: "reviewed" } : void 0,
@@ -769,7 +770,7 @@ const DELEGATION_SYSTEM_PROMPT = [
769
770
  //#region ../agent/src/delegation/toolSource.ts
770
771
  const DELEGATE_TASK_TOOL = "delegate_task";
771
772
  const DELEGATE_TOOL_DESCRIPTION = "Hand one self-contained sub-task to a sub-agent and wait for its summary. One delegation at a time.";
772
- const INPUT_SCHEMA$2 = {
773
+ const INPUT_SCHEMA$4 = {
773
774
  type: "object",
774
775
  properties: {
775
776
  todoId: {
@@ -789,7 +790,7 @@ const INPUT_SCHEMA$2 = {
789
790
  required: ["todoId", "task"],
790
791
  additionalProperties: false
791
792
  };
792
- function toolError$4(code, message) {
793
+ function toolError$6(code, message) {
793
794
  return {
794
795
  ok: false,
795
796
  content: [],
@@ -824,12 +825,12 @@ function createDelegationToolSource(options) {
824
825
  const call = async (_name, args) => {
825
826
  const todoId = args["todoId"];
826
827
  const task = args["task"];
827
- if (typeof todoId !== "string" || todoId === "") return toolError$4("VALIDATION_FAILED", "The \"todoId\" argument is required and must be a string");
828
- if (typeof task !== "string" || task.trim() === "") return toolError$4("VALIDATION_FAILED", "The \"task\" argument is required and must be a non-empty string");
828
+ if (typeof todoId !== "string" || todoId === "") return toolError$6("VALIDATION_FAILED", "The \"todoId\" argument is required and must be a string");
829
+ if (typeof task !== "string" || task.trim() === "") return toolError$6("VALIDATION_FAILED", "The \"task\" argument is required and must be a non-empty string");
829
830
  const rawTools = args["allowedTools"];
830
831
  let allowedTools;
831
832
  if (rawTools !== void 0) {
832
- if (!Array.isArray(rawTools) || rawTools.some((entry) => typeof entry !== "string")) return toolError$4("VALIDATION_FAILED", "The \"allowedTools\" argument must be an array of tool names");
833
+ if (!Array.isArray(rawTools) || rawTools.some((entry) => typeof entry !== "string")) return toolError$6("VALIDATION_FAILED", "The \"allowedTools\" argument must be an array of tool names");
833
834
  allowedTools = rawTools;
834
835
  }
835
836
  try {
@@ -861,7 +862,7 @@ function createDelegationToolSource(options) {
861
862
  }]
862
863
  };
863
864
  } catch (e) {
864
- return toolError$4(e instanceof Error && "code" in e ? String(e.code) : "DELEGATION_FAILED", e instanceof Error ? e.message : String(e));
865
+ return toolError$6(e instanceof Error && "code" in e ? String(e.code) : "DELEGATION_FAILED", e instanceof Error ? e.message : String(e));
865
866
  }
866
867
  };
867
868
  return {
@@ -871,7 +872,7 @@ function createDelegationToolSource(options) {
871
872
  listToolSpecs: () => Promise.resolve([{
872
873
  name: DELEGATE_TASK_TOOL,
873
874
  description: DELEGATE_TOOL_DESCRIPTION,
874
- inputSchema: INPUT_SCHEMA$2
875
+ inputSchema: INPUT_SCHEMA$4
875
876
  }]),
876
877
  canHandle: (name) => name === DELEGATE_TASK_TOOL,
877
878
  argCaptureTrust: (name) => name === "delegate_task" ? { tier: "reviewed" } : void 0,
@@ -1274,7 +1275,7 @@ const PERCEPTION_SYSTEM_PROMPT = [
1274
1275
  //#endregion
1275
1276
  //#region ../agent/src/perception/toolSource.ts
1276
1277
  const PERCEIVE_PAGE_TOOL = "perceive_page";
1277
- const DESCRIPTION$1 = "Read the parts of the current page the host made readable, as a structured accessibility outline. Read-only: it cannot click, type, submit or navigate.";
1278
+ const DESCRIPTION$4 = "Read the parts of the current page the host made readable, as a structured accessibility outline. Read-only: it cannot click, type, submit or navigate.";
1278
1279
  /**
1279
1280
  * 两个参数,且它们都扩不出一寸新范围(AC-10.8 的判据由 0.14.0 分册 22 §3 修订)。
1280
1281
  *
@@ -1287,7 +1288,7 @@ const DESCRIPTION$1 = "Read the parts of the current page the host made readable
1287
1288
  * 「模型看到的工具契约」随宿主配置漂移,同一段对话里换个设置就变一次形状,
1288
1289
  * 而模型的历史消息里还留着旧形状。宁可多一个恒定存在、关闭时明确拒绝的参数。
1289
1290
  */
1290
- const INPUT_SCHEMA$1 = {
1291
+ const INPUT_SCHEMA$3 = {
1291
1292
  type: "object",
1292
1293
  properties: {
1293
1294
  cursor: {
@@ -1308,7 +1309,7 @@ const SUBTREE_CODES = /* @__PURE__ */ new Set([
1308
1309
  "PERCEPTION_SUBTREE_OUT_OF_SCOPE",
1309
1310
  "PERCEPTION_SUBTREE_UNKNOWN_REF"
1310
1311
  ]);
1311
- function toolError$3(code, message) {
1312
+ function toolError$5(code, message) {
1312
1313
  return {
1313
1314
  ok: false,
1314
1315
  content: [],
@@ -1400,8 +1401,8 @@ function createPagePerceptionToolSource(options) {
1400
1401
  kind: "page-perception",
1401
1402
  listToolSpecs: () => Promise.resolve(policy.enabled ? [{
1402
1403
  name: PERCEIVE_PAGE_TOOL,
1403
- description: DESCRIPTION$1,
1404
- inputSchema: INPUT_SCHEMA$1
1404
+ description: DESCRIPTION$4,
1405
+ inputSchema: INPUT_SCHEMA$3
1405
1406
  }] : []),
1406
1407
  systemPrompt: () => Promise.resolve(policy.enabled ? PERCEPTION_SYSTEM_PROMPT : void 0),
1407
1408
  canHandle: (name) => name === PERCEIVE_PAGE_TOOL,
@@ -1415,11 +1416,11 @@ function createPagePerceptionToolSource(options) {
1415
1416
  };
1416
1417
  const cursor = args["cursor"];
1417
1418
  const ref = args["ref"];
1418
- if (ref !== void 0 && typeof ref !== "string") return toolError$3("PERCEPTION_REF_INVALID", "perceive_page needs \"ref\" to be a string when it is present.");
1419
+ if (ref !== void 0 && typeof ref !== "string") return toolError$5("PERCEPTION_REF_INVALID", "perceive_page needs \"ref\" to be a string when it is present.");
1419
1420
  if (cursor !== void 0) {
1420
1421
  const index = typeof cursor === "string" ? pending?.cursors.get(cursor) : void 0;
1421
- if (!paged || pending === void 0 || index === void 0 || pending.runId !== context?.runId) return toolError$3("PERCEPTION_CURSOR_INVALID", CURSOR_REJECTED);
1422
- if (ref !== void 0 && ref !== pending.subtreeRef) return toolError$3("PERCEPTION_CURSOR_INVALID", CURSOR_REJECTED);
1422
+ if (!paged || pending === void 0 || index === void 0 || pending.runId !== context?.runId) return toolError$5("PERCEPTION_CURSOR_INVALID", CURSOR_REJECTED);
1423
+ if (ref !== void 0 && ref !== pending.subtreeRef) return toolError$5("PERCEPTION_CURSOR_INVALID", CURSOR_REJECTED);
1423
1424
  return resultOf(pending, index);
1424
1425
  }
1425
1426
  try {
@@ -1481,8 +1482,8 @@ function createPagePerceptionToolSource(options) {
1481
1482
  return resultOf(pending, 0);
1482
1483
  } catch (e) {
1483
1484
  const code = e instanceof WebSkillError ? e.code : void 0;
1484
- if (code !== void 0 && SUBTREE_CODES.has(code)) return toolError$3(code, messageOf(e));
1485
- return toolError$3("PERCEPTION_FAILED", `perceive_page failed: ${messageOf(e)}`);
1485
+ if (code !== void 0 && SUBTREE_CODES.has(code)) return toolError$5(code, messageOf(e));
1486
+ return toolError$5("PERCEPTION_FAILED", `perceive_page failed: ${messageOf(e)}`);
1486
1487
  }
1487
1488
  }
1488
1489
  };
@@ -1655,7 +1656,7 @@ const RECORD_LIMIT$1 = 50;
1655
1656
  function sanitizeDescription(value) {
1656
1657
  return sanitizeUntrustedLine(value, DESCRIPTION_LIMIT);
1657
1658
  }
1658
- function isRecord(value) {
1659
+ function isRecord$2(value) {
1659
1660
  return typeof value === "object" && value !== null;
1660
1661
  }
1661
1662
  /**
@@ -1665,7 +1666,7 @@ function isRecord(value) {
1665
1666
  * 标成后者会让读代码的人以为它已经被验证过了。
1666
1667
  */
1667
1668
  function validate(raw, provenance) {
1668
- if (!isRecord(raw)) return void 0;
1669
+ if (!isRecord$2(raw)) return void 0;
1669
1670
  const { id, kind, description, target } = raw;
1670
1671
  if (typeof id !== "string" || !ID_PATTERN.test(id)) return void 0;
1671
1672
  if (typeof kind !== "string" || !OFFERABLE_KINDS.includes(kind)) return void 0;
@@ -2099,19 +2100,21 @@ const PAGE_ACTION_SYSTEM_PROMPT = [
2099
2100
  "A reference expires as soon as the page is perceived again; perceive first, then act.",
2100
2101
  "After an action, the result tells you where it led. \"openedTab\" means a new page opened — switch to it with focus_page_tab to read it. \"navigated\" means this page itself moved to a new address. \"elevatedModal\" means a dialog opened on this page. If none of the three appear, the page did not change.",
2101
2102
  "Use the \"back\" action to return to the previous page in this page's history. It takes no \"ref\" and no \"value\", and it only affects the page you are currently on. After going back, always perceive_page again: every reference from before the action is dead.",
2102
- "Use the \"scroll\" action only when the page itself says there is more than you can see (for example a list that says it has 20 rows but shows 13). Pass the \"ref\" of any element inside the region you want to scroll and a \"value\" of \"down\" or \"up\"; the region is found for you, so do not look for a reference to the scrollbar or the container. The result carries \"scrolled\": when \"atEnd\" is true there is nothing further in that direction — stop scrolling and answer with what you have. Perceive the page again after each scroll: rows that left the screen no longer exist and their references are dead."
2103
+ "Use the \"scroll\" action only when the page itself says there is more than you can see (for example a list that says it has 20 rows but shows 13). Pass the \"ref\" of any element inside the region you want to scroll and a \"value\" of \"down\" or \"up\"; the region is found for you, so do not look for a reference to the scrollbar or the container. The result carries \"scrolled\": when \"atEnd\" is true there is nothing further in that direction — stop scrolling and answer with what you have. Perceive the page again after each scroll: rows that left the screen no longer exist and their references are dead.",
2104
+ "Use the \"hover\" action when a menu, tooltip or icon only appears while the pointer rests on something — a dropdown that ignores clicks, or a table header whose filter icon is invisible until hovered. Pass the \"ref\" of the thing to hover over and no \"value\". Then perceive_page again: whatever appeared is new and has no reference until you read it. If the result says \"noop\": true, nothing on the page reacted, and hovering again will not help — some pages reveal those panels through a pure CSS effect that only a real pointer can trigger. Say so and offer another route instead of retrying.",
2105
+ "Use the \"drag\" action to drag one element onto another — reordering a list by its drag handle, for example. Pass the \"ref\" of what you are dragging and a \"value\" holding the ref of where it should land. Both must come from a perceive_page result, so perceive first and make sure the destination is actually on screen. You can only express a start and an end; there is no path, speed or pause in between."
2103
2106
  ].join("\n");
2104
2107
 
2105
2108
  //#endregion
2106
2109
  //#region ../agent/src/pageAction/toolSource.ts
2107
2110
  const PAGE_ACTION_TOOL = "act_on_page";
2108
- const DESCRIPTION = "Act on one element of the current page. The target must be a \"ref\" from a fresh perceive_page result; selectors are rejected. Each call asks the user to confirm before anything happens.";
2111
+ const DESCRIPTION$3 = "Act on one element of the current page. The target must be a \"ref\" from a fresh perceive_page result; selectors are rejected. Each call asks the user to confirm before anything happens.";
2109
2112
  /**
2110
2113
  * 入参里**没有** `scope` / `selector` / `include` / `frame` 一类字段(AC-23.2):
2111
2114
  * 工具一旦接受它们,「能操作哪里」就有了一条从模型输出流进来的路径。
2112
2115
  * 模型能表达的只有「在哪个句柄上做哪个动作」。
2113
2116
  */
2114
- const INPUT_SCHEMA = {
2117
+ const INPUT_SCHEMA$2 = {
2115
2118
  type: "object",
2116
2119
  properties: {
2117
2120
  ref: {
@@ -2124,13 +2127,13 @@ const INPUT_SCHEMA = {
2124
2127
  },
2125
2128
  value: {
2126
2129
  type: "string",
2127
- description: "Text for \"fill\", the option name for \"select\", \"true\"/\"false\" for \"set\", or \"down\"/\"up\" for \"scroll\""
2130
+ description: "Text for \"fill\", the option name for \"select\", \"true\"/\"false\" for \"set\", \"down\"/\"up\" for \"scroll\", or the destination ref for \"drag\". Not used by \"hover\" or \"attach\"."
2128
2131
  }
2129
2132
  },
2130
2133
  required: ["action"],
2131
2134
  additionalProperties: false
2132
2135
  };
2133
- function toolError$2(code, message) {
2136
+ function toolError$4(code, message) {
2134
2137
  return {
2135
2138
  ok: false,
2136
2139
  content: [],
@@ -2153,8 +2156,8 @@ function createPageActionToolSource(options) {
2153
2156
  kind: "page-action",
2154
2157
  listToolSpecs: () => Promise.resolve(policy.enabled ? [{
2155
2158
  name: PAGE_ACTION_TOOL,
2156
- description: DESCRIPTION,
2157
- inputSchema: INPUT_SCHEMA
2159
+ description: DESCRIPTION$3,
2160
+ inputSchema: INPUT_SCHEMA$2
2158
2161
  }] : []),
2159
2162
  systemPrompt: () => Promise.resolve(policy.enabled ? PAGE_ACTION_SYSTEM_PROMPT : void 0),
2160
2163
  canHandle: (name) => name === PAGE_ACTION_TOOL,
@@ -2180,7 +2183,7 @@ function createPageActionToolSource(options) {
2180
2183
  },
2181
2184
  call: async (_name, args, context) => {
2182
2185
  const action = args["action"];
2183
- if (typeof action !== "string" || !PAGE_ACTION_KINDS.includes(action)) return toolError$2("VALIDATION_FAILED", `act_on_page needs one of ${PAGE_ACTION_KINDS.join(" / ")}.`);
2186
+ if (typeof action !== "string" || !PAGE_ACTION_KINDS.includes(action)) return toolError$4("VALIDATION_FAILED", `act_on_page needs one of ${PAGE_ACTION_KINDS.join(" / ")}.`);
2184
2187
  const runContext = { ...context?.runId !== void 0 ? { runId: context.runId } : {} };
2185
2188
  const run = async (request) => {
2186
2189
  try {
@@ -2197,25 +2200,25 @@ function createPageActionToolSource(options) {
2197
2200
  } }
2198
2201
  };
2199
2202
  } catch (e) {
2200
- return toolError$2(e instanceof WebSkillError ? e.code : "PAGE_ACTION_FAILED", messageOf(e));
2203
+ return toolError$4(e instanceof WebSkillError ? e.code : "PAGE_ACTION_FAILED", messageOf(e));
2201
2204
  }
2202
2205
  };
2203
2206
  if (action === "back") {
2204
- if ("ref" in args || "value" in args) return toolError$2("VALIDATION_FAILED", "act_on_page with \"back\" takes no \"ref\" and no \"value\".");
2207
+ if ("ref" in args || "value" in args) return toolError$4("VALIDATION_FAILED", "act_on_page with \"back\" takes no \"ref\" and no \"value\".");
2205
2208
  return await run({
2206
2209
  ref: PAGE_DOCUMENT_REF,
2207
2210
  action: "back"
2208
2211
  });
2209
2212
  }
2210
2213
  const ref = args["ref"];
2211
- if (typeof ref !== "string") return toolError$2("VALIDATION_FAILED", `act_on_page needs a "ref" string when the action is "${action}".`);
2214
+ if (typeof ref !== "string") return toolError$4("VALIDATION_FAILED", `act_on_page needs a "ref" string when the action is "${action}".`);
2212
2215
  const value = args["value"];
2213
2216
  if ([
2214
2217
  "fill",
2215
2218
  "select",
2216
2219
  "set"
2217
- ].includes(action) && typeof value !== "string") return toolError$2("VALIDATION_FAILED", `act_on_page needs a "value" string when the action is "${action}".`);
2218
- if (action === "scroll" && value !== "down" && value !== "up") return toolError$2("VALIDATION_FAILED", "act_on_page with \"scroll\" needs a \"value\" of \"down\" or \"up\".");
2220
+ ].includes(action) && typeof value !== "string") return toolError$4("VALIDATION_FAILED", `act_on_page needs a "value" string when the action is "${action}".`);
2221
+ if (action === "scroll" && value !== "down" && value !== "up") return toolError$4("VALIDATION_FAILED", "act_on_page with \"scroll\" needs a \"value\" of \"down\" or \"up\".");
2219
2222
  return await run({
2220
2223
  ref,
2221
2224
  action,
@@ -2457,7 +2460,7 @@ const SPECS = [
2457
2460
  inputSchema: TAB_SCHEMA
2458
2461
  }
2459
2462
  ];
2460
- function toolError$1(code, message) {
2463
+ function toolError$3(code, message) {
2461
2464
  return {
2462
2465
  ok: false,
2463
2466
  content: [],
@@ -2492,7 +2495,7 @@ function createTabWorksetToolSource(options) {
2492
2495
  }]
2493
2496
  };
2494
2497
  const handle = args["tab"];
2495
- if (typeof handle !== "string" || handle === "") return toolError$1("VALIDATION_FAILED", `${name} needs a "tab" handle from list_page_tabs.`);
2498
+ if (typeof handle !== "string" || handle === "") return toolError$3("VALIDATION_FAILED", `${name} needs a "tab" handle from list_page_tabs.`);
2496
2499
  return {
2497
2500
  ok: true,
2498
2501
  content: [{
@@ -2501,7 +2504,7 @@ function createTabWorksetToolSource(options) {
2501
2504
  }]
2502
2505
  };
2503
2506
  } catch (e) {
2504
- return toolError$1(e instanceof WebSkillError ? e.code : "PAGE_TAB_FAILED", messageOf(e));
2507
+ return toolError$3(e instanceof WebSkillError ? e.code : "PAGE_TAB_FAILED", messageOf(e));
2505
2508
  }
2506
2509
  }
2507
2510
  };
@@ -2517,12 +2520,12 @@ function formatSize(bytes) {
2517
2520
  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
2518
2521
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
2519
2522
  }
2520
- /** 两种待抽取格式共用 `document-text` 这一种 kind,选抽取器时要按格式再分一次 */
2521
- function isSpreadsheet(contentType, name) {
2522
- const type = contentType.toLowerCase();
2523
- if (type !== "") return type === XLSX_MIME;
2524
- return name.toLowerCase().endsWith(".xlsx");
2525
- }
2523
+ /** `document-text` 下的三种格式各自的抽取器与称呼 */
2524
+ const EXTRACTED_LABEL = {
2525
+ docx: "Word documents",
2526
+ xlsx: "Excel workbooks",
2527
+ pptx: "PowerPoint presentations"
2528
+ };
2526
2529
  /**
2527
2530
  * 读取本机下载文件的策略(0.14.0 分册 20)。
2528
2531
  *
@@ -2622,6 +2625,42 @@ var DownloadedFilePolicy = class {
2622
2625
  });
2623
2626
  return result;
2624
2627
  }
2628
+ /**
2629
+ * 取原始字节,不做形态化(0.21.0 分册 22 · FR-22.4)。
2630
+ *
2631
+ * 授权卡、审计留痕与 `read()` 完全一致——逐单元读文档要的是字节本身,
2632
+ * 但绝不能因此绕过策略:那等于给「读用户本机磁盘」开第二个没有授权的入口。
2633
+ * @experimental
2634
+ */
2635
+ async readBytes(id) {
2636
+ const reader = this.#reader();
2637
+ const entry = await this.#resolve(id);
2638
+ const approved = await this.#authorize("read", entry);
2639
+ if (approved === false) {
2640
+ const reason = `Reading "${entry.name}" was declined by the user.`;
2641
+ await this.#record({
2642
+ action: "read",
2643
+ ...describe(entry),
2644
+ approved: false,
2645
+ ok: false,
2646
+ reason
2647
+ });
2648
+ throw new WebSkillError("TOOL_DENIED", reason);
2649
+ }
2650
+ const { contentType, bytes } = await reader.read(id);
2651
+ await this.#record({
2652
+ action: "read",
2653
+ ...describe(entry),
2654
+ contentType,
2655
+ approved,
2656
+ ok: true
2657
+ });
2658
+ return {
2659
+ entry,
2660
+ contentType,
2661
+ bytes
2662
+ };
2663
+ }
2625
2664
  async #materialize(entry, contentType, kind, bytes) {
2626
2665
  if (kind === "text" || kind === "document-text") {
2627
2666
  const body = kind === "text" ? new TextDecoder().decode(bytes) : await this.#extract(contentType, entry.name)(bytes);
@@ -2629,15 +2668,10 @@ var DownloadedFilePolicy = class {
2629
2668
  entry,
2630
2669
  contentType,
2631
2670
  kind,
2632
- text: formatAttachmentText({
2633
- name: entry.name,
2634
- contentType,
2635
- body,
2636
- kind,
2637
- origin: "download"
2638
- })
2671
+ text: this.#label(entry, contentType, body, kind)
2639
2672
  };
2640
2673
  }
2674
+ if (kind === "file" && this.#overDocumentLimit(bytes)) return await this.#pdfAsText(entry, contentType, bytes);
2641
2675
  return {
2642
2676
  entry,
2643
2677
  contentType,
@@ -2645,14 +2679,57 @@ var DownloadedFilePolicy = class {
2645
2679
  bytes
2646
2680
  };
2647
2681
  }
2682
+ /** 与手动上传共用同一个格式化器:两条入口的产出必须逐字一致(AC-20.6) */
2683
+ #label(entry, contentType, body, kind) {
2684
+ return formatAttachmentText({
2685
+ name: entry.name,
2686
+ contentType,
2687
+ body,
2688
+ kind,
2689
+ origin: "download"
2690
+ });
2691
+ }
2692
+ /** base64 编码后才是请求体里的长度,量原始字节会漏掉那三分之一(与上传侧同口径) */
2693
+ #overDocumentLimit(bytes) {
2694
+ const max = this.#options.maxDocumentBytes?.();
2695
+ return max !== void 0 && Math.ceil(bytes.length / 3) * 4 > max;
2696
+ }
2697
+ /**
2698
+ * 超阈值 PDF 退到抽取文本(AC-21.2),抽不出就**拒绝并说明**(AC-21.3):
2699
+ * 半份原件是坏文件,半份抽取文本会让模型以为自己读全了。与上传侧 `#routeDocument` 同一套判断。
2700
+ */
2701
+ async #pdfAsText(entry, contentType, bytes) {
2702
+ const tooLarge = `${entry.name} is ${formatSize(bytes.length)}, over the request limit for sending a document as is. It was not truncated, because a partial document would be unusable.`;
2703
+ const extract = this.#options.pdfExtractor;
2704
+ if (extract === void 0) throw new WebSkillError("TOOL_UNSUPPORTED", `${tooLarge} This environment has no PDF text extractor either, so no fallback is available. Tell the user to switch to a model that accepts large documents.`);
2705
+ let body;
2706
+ try {
2707
+ body = await extract(bytes);
2708
+ } catch (e) {
2709
+ throw new WebSkillError("TOOL_UNSUPPORTED", `${tooLarge} Text extraction failed as well: ${e instanceof Error ? e.message : String(e)}. Ask the user for a smaller file or a specific page range.`);
2710
+ }
2711
+ if (body.trim() === "") throw new WebSkillError("TOOL_UNSUPPORTED", `${tooLarge} It has no text layer either — most likely a scan or an image-only export — so nothing could be extracted. Tell the user this instead of guessing what it contains.`);
2712
+ return {
2713
+ entry,
2714
+ contentType,
2715
+ kind: "document-text",
2716
+ text: this.#label(entry, contentType, body, "document-text")
2717
+ };
2718
+ }
2648
2719
  #extract(contentType, name) {
2649
- const extract = isSpreadsheet(contentType, name) ? this.#options.xlsxExtractor : this.#options.docxExtractor;
2720
+ const extract = this.#extractorFor(contentType, name);
2650
2721
  if (extract === void 0) throw new WebSkillError("TOOL_UNSUPPORTED", this.#noExtractorReason(contentType, name));
2651
2722
  return extract;
2652
2723
  }
2724
+ #extractorFor(contentType, name) {
2725
+ const format = extractedDocumentFormat(contentType, name);
2726
+ if (format === "xlsx") return this.#options.xlsxExtractor;
2727
+ if (format === "pptx") return this.#options.pptxExtractor;
2728
+ return this.#options.docxExtractor;
2729
+ }
2653
2730
  #noExtractorReason(contentType, name) {
2654
- const excel = isSpreadsheet(contentType, name);
2655
- return `${excel ? "Excel workbooks" : "Word documents"} cannot be read in this environment: no ${excel ? "xlsx" : "docx"} text extractor is wired up.`;
2731
+ const format = extractedDocumentFormat(contentType, name);
2732
+ return `${EXTRACTED_LABEL[format]} cannot be read in this environment: no ${format} text extractor is wired up.`;
2656
2733
  }
2657
2734
  /** 可读性判定与附件分类器同源(AC-20.17);返回 `undefined` 即可读 */
2658
2735
  async #unreadableReason(contentType, name) {
@@ -2660,7 +2737,7 @@ var DownloadedFilePolicy = class {
2660
2737
  if (kind === void 0) return `Its type "${contentType === "" ? "unknown" : contentType}" cannot be read. ${READABLE_TYPES}`;
2661
2738
  if (kind === "image" && await this.#options.imageCapable?.() !== true) return IMAGE_UNREADABLE;
2662
2739
  if (kind === "document-text") {
2663
- if ((isSpreadsheet(contentType, name) ? this.#options.xlsxExtractor : this.#options.docxExtractor) === void 0) return this.#noExtractorReason(contentType, name);
2740
+ if (this.#extractorFor(contentType, name) === void 0) return this.#noExtractorReason(contentType, name);
2664
2741
  }
2665
2742
  }
2666
2743
  #unreadable(entry, reason) {
@@ -2756,8 +2833,8 @@ const READ_INPUT_SCHEMA = {
2756
2833
  required: ["id"],
2757
2834
  additionalProperties: false
2758
2835
  };
2759
- const SYSTEM_PROMPT = "You can list and read files the user downloaded in this browser with list_downloaded_files and read_downloaded_file. Every call asks the user for permission, so only use them when the task actually needs a local file. Never guess an id: always list first, and never treat the listing as evidence about files you were not shown.";
2760
- function toolError(e) {
2836
+ const SYSTEM_PROMPT$3 = "You can list and read files the user downloaded in this browser with list_downloaded_files and read_downloaded_file. They cover the browser download folder and nothing else. A file the user attached to a message is already in this conversation and is reachable by the id given with it, so never go looking for an attachment here. Every call asks the user for permission, so only use them when the task actually needs a local file. Never guess an id: always list first, and never treat the listing as evidence about files you were not shown.";
2837
+ function toolError$2(e) {
2761
2838
  return {
2762
2839
  ok: false,
2763
2840
  content: [],
@@ -2787,7 +2864,7 @@ function createDownloadedFileToolSource(options) {
2787
2864
  description: READ_DESCRIPTION,
2788
2865
  inputSchema: READ_INPUT_SCHEMA
2789
2866
  }] : []),
2790
- systemPrompt: () => Promise.resolve(policy.enabled ? SYSTEM_PROMPT : void 0),
2867
+ systemPrompt: () => Promise.resolve(policy.enabled ? SYSTEM_PROMPT$3 : void 0),
2791
2868
  canHandle: (name) => name === "list_downloaded_files" || name === "read_downloaded_file",
2792
2869
  argCaptureTrust: (name) => name === "list_downloaded_files" || name === "read_downloaded_file" ? { tier: "reviewed" } : void 0,
2793
2870
  call: async (name, args) => {
@@ -2806,7 +2883,7 @@ function createDownloadedFileToolSource(options) {
2806
2883
  content: readContent(await policy.read(id))
2807
2884
  };
2808
2885
  } catch (e) {
2809
- return toolError(e);
2886
+ return toolError$2(e);
2810
2887
  }
2811
2888
  }
2812
2889
  };
@@ -2847,4 +2924,1025 @@ function readContent(result) {
2847
2924
  }
2848
2925
 
2849
2926
  //#endregion
2850
- export { CLOSE_PAGE_TAB_TOOL, DEFAULT_MAX_DATA_SOURCE_BYTES, DELEGATE_TASK_TOOL, DELEGATION_SYSTEM_PROMPT, DOWNLOAD_WAIT_DEFAULT_MS, DOWNLOAD_WAIT_MAX_MS, DOWNLOAD_WAIT_MIN_MS, DataSourceCandidatePolicy, DataSourcePolicy, DelegationOrchestrator, DownloadedFilePolicy, FOCUS_PAGE_TAB_TOOL, GENERATE_SKILL_TOOL, LIST_DOWNLOADED_FILES_TOOL, LIST_PAGE_TABS_TOOL, MANAGE_TODO_TOOL, PAGE_ACTION_KINDS, PAGE_ACTION_SYSTEM_PROMPT, PAGE_ACTION_TOOL, PAGE_DOCUMENT_REF, PERCEIVE_PAGE_TOOL, PERCEPTION_PAGING_DEFAULTS, PERCEPTION_SYSTEM_PROMPT, PageActionPolicy, PagePerceptionPolicy, READ_DOWNLOADED_FILE_TOOL, SKILL_GENERATION_SYSTEM_PROMPT, SkillGenerator, TAB_WORKSET_DEFAULT_MAX, TAB_WORKSET_MAX_MAX, TAB_WORKSET_MIN_MAX, TAB_WORKSET_SYSTEM_PROMPT, TODO_SYSTEM_PROMPT, TabWorksetPolicy, TodoStore, clampDownloadWaitMs, clampWorksetMax, createDelegationToolSource, createDownloadedFileToolSource, createHttpDataSourceTransport, createPageActionToolSource, createPagePerceptionToolSource, createSkillGenerationToolSource, createTabWorksetToolSource, createTodoToolSource, frameLabel, frameSteps, toActionFrameScopes, toFrameScopes, withDelegationOrigin };
2927
+ //#region ../agent/src/spreadsheet/toolSource.ts
2928
+ const EXPORT_SPREADSHEET_TOOL = "export_spreadsheet";
2929
+ const DESCRIPTION$2 = "Write tabular data — and, when they belong in the sheet, pictures — to an Excel (.xlsx) file the user can download.";
2930
+ const CELL_SCHEMA = {
2931
+ anyOf: [
2932
+ { type: "string" },
2933
+ { type: "number" },
2934
+ { type: "boolean" },
2935
+ { type: "null" }
2936
+ ],
2937
+ description: "Text is never re-parsed as a number or a date: \"0123\" stays \"0123\". Use null for an empty cell."
2938
+ };
2939
+ /**
2940
+ * 表里的图。只在宿主接了 `resolveImage` 时才并进 schema——
2941
+ * 接不了字节却把字段给出去,等于邀请模型填一个填了也不算数的东西。
2942
+ */
2943
+ const IMAGES_SCHEMA = {
2944
+ type: "array",
2945
+ maxItems: SPREADSHEET_LIMITS.imagesPerSheet,
2946
+ description: "Pictures placed on this sheet. Use this instead of telling the user to paste images in themselves, and instead of handing out download links for them.",
2947
+ items: {
2948
+ type: "object",
2949
+ properties: {
2950
+ ref: {
2951
+ type: "string",
2952
+ description: "Where the picture comes from: \"artifact:<file name>\" for something produced earlier in this conversation, or \"upload:<id or file name>\" for a picture the user attached. Never base64."
2953
+ },
2954
+ anchor: {
2955
+ type: "object",
2956
+ description: "Top-left cell the picture sits on. Both numbers are 1-based, so row 1 is the header row.",
2957
+ properties: {
2958
+ row: {
2959
+ type: "integer",
2960
+ minimum: 1
2961
+ },
2962
+ column: {
2963
+ type: "integer",
2964
+ minimum: 1
2965
+ }
2966
+ },
2967
+ required: ["row", "column"],
2968
+ additionalProperties: false
2969
+ },
2970
+ width: {
2971
+ type: "integer",
2972
+ minimum: 1,
2973
+ description: "Displayed width in pixels."
2974
+ },
2975
+ height: {
2976
+ type: "integer",
2977
+ minimum: 1,
2978
+ description: "Displayed height in pixels."
2979
+ },
2980
+ alt: {
2981
+ type: "string",
2982
+ description: "Short caption, used as the picture name inside the workbook."
2983
+ }
2984
+ },
2985
+ required: [
2986
+ "ref",
2987
+ "anchor",
2988
+ "width",
2989
+ "height"
2990
+ ],
2991
+ additionalProperties: false
2992
+ }
2993
+ };
2994
+ function inputSchema(withImages) {
2995
+ return {
2996
+ type: "object",
2997
+ properties: {
2998
+ fileName: {
2999
+ type: "string",
3000
+ description: "Name for the workbook, for example \"sales-2026.xlsx\". A missing .xlsx suffix is added for you."
3001
+ },
3002
+ spec: {
3003
+ type: "object",
3004
+ properties: { sheets: {
3005
+ type: "array",
3006
+ minItems: 1,
3007
+ maxItems: SPREADSHEET_LIMITS.sheets,
3008
+ items: {
3009
+ type: "object",
3010
+ properties: {
3011
+ name: {
3012
+ type: "string",
3013
+ description: "Sheet name: at most 31 characters, and none of : \\ / ? * [ ]"
3014
+ },
3015
+ columns: {
3016
+ type: "array",
3017
+ items: { type: "string" },
3018
+ description: "Optional header row. When given it becomes the first, bold row."
3019
+ },
3020
+ rows: {
3021
+ type: "array",
3022
+ maxItems: SPREADSHEET_LIMITS.rowsPerSheet,
3023
+ items: {
3024
+ type: "array",
3025
+ maxItems: SPREADSHEET_LIMITS.columnsPerSheet,
3026
+ items: CELL_SCHEMA
3027
+ }
3028
+ },
3029
+ ...withImages ? { images: IMAGES_SCHEMA } : {}
3030
+ },
3031
+ required: ["name", "rows"],
3032
+ additionalProperties: false
3033
+ }
3034
+ } },
3035
+ required: ["sheets"],
3036
+ additionalProperties: false
3037
+ }
3038
+ },
3039
+ required: ["fileName", "spec"],
3040
+ additionalProperties: false
3041
+ };
3042
+ }
3043
+ function systemPrompt$1(withImages) {
3044
+ return "Use export_spreadsheet when the user asks for a downloadable Excel file. The tool returns only a summary; the file itself is delivered to the user as a card. Do not re-read the file you just wrote. Cell values are written literally — the tool never turns text into a number or a date, and it writes no styling, formulas, merged cells or charts." + (withImages ? " Pictures do go in: put them in the sheet's \"images\" with a ref and an anchor cell. A picture the user attached to a message is already in this conversation — reference it as \"upload:<id or file name>\" and do not go looking for it on the page or in the download list first. When the user asks for a workbook of pictures, put the pictures in it — do not deliver the images separately and ask the user to insert them." : "");
3045
+ }
3046
+ function toolError$1(e) {
3047
+ return {
3048
+ ok: false,
3049
+ content: [],
3050
+ error: {
3051
+ code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
3052
+ message: messageOf(e)
3053
+ }
3054
+ };
3055
+ }
3056
+ const isRecord$1 = (v) => typeof v === "object" && v !== null;
3057
+ /**
3058
+ * FR-10.3a 的两条分支。忘写后缀是模型的高频行为,为它打断一整轮不划算;
3059
+ * 写成 `.csv` 则是**意图冲突**而不是疏漏,必须拒。
3060
+ *
3061
+ * 路径安全不在这里做:`writeArtifact` 那一条(`resolveInsideRoot`)是唯一的一份,
3062
+ * FR-10.3b 明确禁止另写一份。
3063
+ */
3064
+ function resolveFileName$1(value) {
3065
+ if (typeof value !== "string" || value.trim() === "") throw new WebSkillError("VALIDATION_FAILED", "export_spreadsheet requires a non-empty \"fileName\".");
3066
+ const fileName = value.trim();
3067
+ const lastSegment = fileName.split(/[\\/]/).pop() ?? "";
3068
+ const dot = lastSegment.lastIndexOf(".");
3069
+ const extension = dot <= 0 ? "" : lastSegment.slice(dot + 1);
3070
+ if (extension === "") return `${fileName}${SPREADSHEET_EXTENSION}`;
3071
+ if (extension.toLowerCase() === "xlsx") return fileName;
3072
+ throw new WebSkillError("VALIDATION_FAILED", `export_spreadsheet writes .xlsx files; "${fileName}" asks for .${extension}. Drop the suffix or use .xlsx.`);
3073
+ }
3074
+ function summarize(spec) {
3075
+ const sheets = spec.sheets;
3076
+ const imageCount = sheets.reduce((total, sheet) => total + (sheet.images?.length ?? 0), 0);
3077
+ return {
3078
+ sheetCount: sheets.length,
3079
+ rowCount: sheets.reduce((total, sheet) => total + sheet.rows.length, 0),
3080
+ ...imageCount > 0 ? { imageCount } : {}
3081
+ };
3082
+ }
3083
+ /**
3084
+ * 把表格导出接到既有工具协议上。
3085
+ *
3086
+ * 做成 `ExternalToolSource` 而不是 `AgentLoop` 的内置数组(DV-4):本工具需要
3087
+ * `argCaptureTrust` 与 `captureArgs` 两个钩子,内置数组里没有这两个概念。
3088
+ * @experimental
3089
+ */
3090
+ function createSpreadsheetToolSource(options) {
3091
+ const { writeArtifact, resolveImage } = options;
3092
+ const withImages = resolveImage !== void 0;
3093
+ const schema = inputSchema(withImages);
3094
+ const prompt = systemPrompt$1(withImages);
3095
+ return {
3096
+ kind: "spreadsheet",
3097
+ listToolSpecs: () => Promise.resolve(writeArtifact ? [{
3098
+ name: EXPORT_SPREADSHEET_TOOL,
3099
+ description: DESCRIPTION$2,
3100
+ inputSchema: schema
3101
+ }] : []),
3102
+ systemPrompt: () => Promise.resolve(writeArtifact ? prompt : void 0),
3103
+ canHandle: (name) => name === EXPORT_SPREADSHEET_TOOL,
3104
+ captureArgs: (_name, args) => {
3105
+ const spec = args["spec"];
3106
+ const sheets = isRecord$1(spec) && Array.isArray(spec["sheets"]) ? spec["sheets"] : [];
3107
+ const rowCount = sheets.reduce((total, sheet) => total + (isRecord$1(sheet) && Array.isArray(sheet["rows"]) ? sheet["rows"].length : 0), 0);
3108
+ return {
3109
+ ...typeof args["fileName"] === "string" ? { fileName: args["fileName"] } : {},
3110
+ sheetCount: sheets.length,
3111
+ rowCount
3112
+ };
3113
+ },
3114
+ call: async (name, args, context) => {
3115
+ try {
3116
+ if (name !== "export_spreadsheet" || !writeArtifact) throw new WebSkillError("TOOL_UNSUPPORTED", `${name} is not available in this host.`);
3117
+ const fileName = resolveFileName$1(args["fileName"]);
3118
+ const spec = args["spec"];
3119
+ const bytes = await encodeSpreadsheet(spec, resolveImage ? (ref) => resolveImage(ref, context) : void 0);
3120
+ const artifact = await writeArtifact(fileName, bytes, SPREADSHEET_MIME_TYPE, context);
3121
+ return {
3122
+ ok: true,
3123
+ content: [{
3124
+ type: "json",
3125
+ data: {
3126
+ path: artifact.path,
3127
+ size: artifact.size,
3128
+ ...summarize(spec)
3129
+ }
3130
+ }]
3131
+ };
3132
+ } catch (e) {
3133
+ return toolError$1(e);
3134
+ }
3135
+ }
3136
+ };
3137
+ }
3138
+
3139
+ //#endregion
3140
+ //#region ../agent/src/template/toolSource.ts
3141
+ /** @experimental */
3142
+ const INSPECT_TEMPLATE_TOOL = "inspect_template";
3143
+ /** @experimental */
3144
+ const FILL_TEMPLATE_TOOL = "fill_template";
3145
+ const XLSX_MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
3146
+ const DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
3147
+ /**
3148
+ * 骨架预算。模板不是文档,格子数是有限的(实测四份模板 32~56 个),
3149
+ * 但没人能保证用户不传一份两千行的台账进来——超了就截断并明说截了。
3150
+ * @experimental
3151
+ */
3152
+ const TEMPLATE_INSPECT_BUDGET = {
3153
+ cells: 400,
3154
+ chars: 2e4
3155
+ };
3156
+ const INSPECT_DESCRIPTION = "Look inside an Excel (.xlsx) or Word (.docx) template the user attached, and report where things are: sheet names, cell addresses and their current text, merged regions; for Word, paragraph indexes and table/row/column coordinates. A cell reported with a \"formula\" is worked out by the template itself; fill the cells it reads instead. Call this before fill_template — you cannot guess a template's layout.";
3157
+ const FILL_DESCRIPTION = "Fill an Excel (.xlsx) or Word (.docx) template the user attached and hand the result back as a downloadable file. Everything the template already contains — styling, column widths, merged cells, formulas, headers and footers — is kept exactly as it was; only the places you name are written.";
3158
+ const TEMPLATE_ID = "The attachment id, or the id from list_downloaded_files.";
3159
+ function inspectSchema(idHint) {
3160
+ return {
3161
+ type: "object",
3162
+ properties: {
3163
+ template: {
3164
+ type: "string",
3165
+ description: templateIdDescription(idHint)
3166
+ },
3167
+ sheet: {
3168
+ type: "string",
3169
+ description: "Excel only: inspect just this sheet. Omit to inspect every sheet."
3170
+ }
3171
+ },
3172
+ required: ["template"],
3173
+ additionalProperties: false
3174
+ };
3175
+ }
3176
+ /**
3177
+ * 取件号的可用形式由**宿主接了什么**决定,所以多出来的那一句由宿主给。
3178
+ * 无条件写死「还可以传在线文档」的后果是:没接那条通路的宿主里,模型会反复尝试一个永远读不到的号。
3179
+ */
3180
+ function templateIdDescription(idHint) {
3181
+ return idHint === void 0 || idHint === "" ? TEMPLATE_ID : `${TEMPLATE_ID} ${idHint}`;
3182
+ }
3183
+ const IMAGE_REF = {
3184
+ type: "string",
3185
+ description: "Where the picture comes from: \"artifact:<file name>\" for something produced earlier in this conversation, or \"upload:<id or file name>\" for a picture the user attached. Never base64."
3186
+ };
3187
+ function fillSchema(withImages, idHint) {
3188
+ return {
3189
+ type: "object",
3190
+ properties: {
3191
+ template: {
3192
+ type: "string",
3193
+ description: templateIdDescription(idHint)
3194
+ },
3195
+ fileName: {
3196
+ type: "string",
3197
+ description: "Name for the file you are producing. The template itself is never modified."
3198
+ },
3199
+ cells: {
3200
+ type: "array",
3201
+ description: "Excel only. Each entry writes one cell. Never write to a cell that inspect_template reported with a \"formula\" — an amount or a total is the template's own calculation, and overwriting it produces a sheet that no longer adds up when anything changes.",
3202
+ items: {
3203
+ type: "object",
3204
+ properties: {
3205
+ sheet: {
3206
+ type: "string",
3207
+ description: "Sheet name. Omit for the first sheet."
3208
+ },
3209
+ address: {
3210
+ type: "string",
3211
+ description: "A1-style address, for example \"B7\"."
3212
+ },
3213
+ value: {
3214
+ type: "string",
3215
+ description: "A plain number such as \"2\" or \"-1.5\" is stored as a number, so formulas and totals in the template add it up. Anything else is stored as text, including \"0123\", \"1,200\" and \"12 kg\" — write amounts as bare digits with no thousands separator, currency sign or unit, or the template's SUM will skip them. Everything is written literally: \"0123\" stays \"0123\". Use \"\" to clear the cell while keeping its formatting."
3216
+ }
3217
+ },
3218
+ required: ["address", "value"],
3219
+ additionalProperties: false
3220
+ }
3221
+ },
3222
+ rows: {
3223
+ type: "array",
3224
+ description: "Excel only. Insert blank rows before writing cells, when the template has fewer detail rows than you need. Formulas, merged regions and the rows below move down with them; addresses in \"cells\" are the positions after the insert.",
3225
+ items: {
3226
+ type: "object",
3227
+ properties: {
3228
+ sheet: { type: "string" },
3229
+ at: {
3230
+ type: "integer",
3231
+ minimum: 1,
3232
+ description: "1-based row number the new rows are inserted before."
3233
+ },
3234
+ count: {
3235
+ type: "integer",
3236
+ minimum: 1
3237
+ },
3238
+ copyStyleFrom: {
3239
+ type: "integer",
3240
+ minimum: 1,
3241
+ description: "Row whose formatting the new rows copy. Defaults to the row just above \"at\"."
3242
+ }
3243
+ },
3244
+ required: ["at", "count"],
3245
+ additionalProperties: false
3246
+ }
3247
+ },
3248
+ texts: {
3249
+ type: "array",
3250
+ description: "Word only. Each entry replaces the WHOLE text of one paragraph or one table cell — not part of it. A header line often holds several fields at once (for example \"No.: ABC-01 Serial: \"), and writing only the field you are changing deletes the others. Copy the text inspect_template reported and change just the part that differs, keeping the spacing that lines the fields up.",
3251
+ items: {
3252
+ type: "object",
3253
+ properties: {
3254
+ paragraph: {
3255
+ type: "integer",
3256
+ minimum: 0,
3257
+ description: "0-based body paragraph index."
3258
+ },
3259
+ table: {
3260
+ type: "integer",
3261
+ minimum: 0,
3262
+ description: "0-based table index."
3263
+ },
3264
+ row: {
3265
+ type: "integer",
3266
+ minimum: 0
3267
+ },
3268
+ column: {
3269
+ type: "integer",
3270
+ minimum: 0
3271
+ },
3272
+ value: {
3273
+ type: "string",
3274
+ description: "Written literally, and it becomes the entire content of the target. Use \"\" to clear the target."
3275
+ }
3276
+ },
3277
+ required: ["value"],
3278
+ additionalProperties: false
3279
+ }
3280
+ },
3281
+ ...withImages ? { images: {
3282
+ type: "array",
3283
+ description: "Pictures to place in the template. Use this instead of telling the user to paste them in themselves. Each picture is scaled down to fit the cell it lands on and never overflows it.",
3284
+ items: {
3285
+ type: "object",
3286
+ properties: {
3287
+ ref: IMAGE_REF,
3288
+ sheet: {
3289
+ type: "string",
3290
+ description: "Excel only."
3291
+ },
3292
+ address: {
3293
+ type: "string",
3294
+ description: "Excel only: A1-style cell the picture sits on."
3295
+ },
3296
+ paragraph: {
3297
+ type: "integer",
3298
+ minimum: 0,
3299
+ description: "Word only."
3300
+ },
3301
+ table: {
3302
+ type: "integer",
3303
+ minimum: 0,
3304
+ description: "Word only."
3305
+ },
3306
+ row: {
3307
+ type: "integer",
3308
+ minimum: 0,
3309
+ description: "Word only."
3310
+ },
3311
+ column: {
3312
+ type: "integer",
3313
+ minimum: 0,
3314
+ description: "Word only."
3315
+ }
3316
+ },
3317
+ required: ["ref"],
3318
+ additionalProperties: false
3319
+ }
3320
+ } } : {}
3321
+ },
3322
+ required: ["template", "fileName"],
3323
+ additionalProperties: false
3324
+ };
3325
+ }
3326
+ function systemPrompt(withImages) {
3327
+ return "When the user gives you a form, a table or a report template and asks you to produce a filled-in copy, fill that template — do not build a new file from scratch and do not describe what to type where. Always call inspect_template first: the addresses and coordinates come from it, never from guesswork, and a wrong address is a wrong file. fill_template returns only a summary; the file is delivered to the user as a card, so do not read it back. The template is left untouched — everything it already contains is preserved, and you only name the places that change. When the template has too few detail rows for the data, insert rows instead of overwriting the rows below." + (withImages ? " A template that reserves space for a photo takes one: reference the picture as \"artifact:<name>\" or \"upload:<id or name>\" and give the cell it belongs in. Do not hand the picture over separately and ask the user to paste it." : "");
3328
+ }
3329
+ function toolError(e) {
3330
+ return {
3331
+ ok: false,
3332
+ content: [],
3333
+ error: {
3334
+ code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
3335
+ message: messageOf(e)
3336
+ }
3337
+ };
3338
+ }
3339
+ const isRecord = (v) => typeof v === "object" && v !== null;
3340
+ /**
3341
+ * 照着模板出文件(0.22.0 分册 42)。
3342
+ *
3343
+ * 两个工具而不是一个:模型得先看见模板的坐标,才谈得上决定往哪儿写。
3344
+ * 合成一个工具就只剩两条路——要么让模型盲填,要么每次把整份骨架塞进填值的返回里。
3345
+ * @experimental
3346
+ */
3347
+ function createTemplateToolSource(options) {
3348
+ const { files, xlsx, docx, writeArtifact, resolveImage } = options;
3349
+ const withImages = resolveImage !== void 0;
3350
+ const inspectInput = inspectSchema(options.idHint);
3351
+ const schema = fillSchema(withImages, options.idHint);
3352
+ const prompt = systemPrompt(withImages);
3353
+ const available = () => xlsx !== void 0 || docx !== void 0;
3354
+ const budgetOf = () => options.budget?.() ?? TEMPLATE_INSPECT_BUDGET;
3355
+ return {
3356
+ kind: "office-template",
3357
+ listToolSpecs: () => {
3358
+ if (!available()) return Promise.resolve([]);
3359
+ const specs = [{
3360
+ name: INSPECT_TEMPLATE_TOOL,
3361
+ description: INSPECT_DESCRIPTION,
3362
+ inputSchema: inspectInput
3363
+ }];
3364
+ if (writeArtifact !== void 0) specs.push({
3365
+ name: FILL_TEMPLATE_TOOL,
3366
+ description: FILL_DESCRIPTION,
3367
+ inputSchema: schema
3368
+ });
3369
+ return Promise.resolve(specs);
3370
+ },
3371
+ systemPrompt: () => Promise.resolve(available() && writeArtifact !== void 0 ? prompt : void 0),
3372
+ canHandle: (name) => name === "inspect_template" || name === "fill_template",
3373
+ argCaptureTrust: (name) => name === "inspect_template" ? { tier: "reviewed" } : void 0,
3374
+ captureArgs: (name, args) => {
3375
+ if (name === "inspect_template") return args;
3376
+ const count = (key) => Array.isArray(args[key]) ? args[key].length : 0;
3377
+ return {
3378
+ ...typeof args["fileName"] === "string" ? { fileName: args["fileName"] } : {},
3379
+ filledCount: count("cells") + count("texts"),
3380
+ insertedRows: (Array.isArray(args["rows"]) ? args["rows"] : []).reduce((total, row) => total + (isRecord(row) && typeof row["count"] === "number" ? row["count"] : 0), 0),
3381
+ imageCount: count("images")
3382
+ };
3383
+ },
3384
+ call: async (name, args, context) => {
3385
+ try {
3386
+ if (name === "inspect_template") return await inspect(args, context);
3387
+ if (name === "fill_template") return await fill(args, context);
3388
+ throw new WebSkillError("TOOL_NOT_FOUND", `Unknown tool ${name}.`);
3389
+ } catch (e) {
3390
+ return toolError(e);
3391
+ }
3392
+ }
3393
+ };
3394
+ async function load(raw, tool, context) {
3395
+ if (typeof raw !== "string" || raw === "") throw new WebSkillError("TOOL_UNSUPPORTED", `${tool} requires the "template" id of an attachment or download.`);
3396
+ const file = await files.read(raw, context);
3397
+ if (file === void 0) throw new WebSkillError("TOOL_UNSUPPORTED", `No readable file with id "${raw}". 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.`);
3398
+ const format = sniffDocumentFormat(file.bytes, file.name);
3399
+ if (format !== "xlsx" && format !== "docx") throw new WebSkillError("TEMPLATE_UNSUPPORTED", `"${file.name}" is not an Excel (.xlsx) or Word (.docx) file, so it cannot be used as a template.`);
3400
+ if ((format === "xlsx" ? xlsx : docx) === void 0) throw new WebSkillError("TEMPLATE_UNSUPPORTED", `This environment cannot fill ${format} templates.`);
3401
+ return {
3402
+ name: file.name,
3403
+ bytes: file.bytes,
3404
+ format
3405
+ };
3406
+ }
3407
+ async function inspect(args, context) {
3408
+ const template = await load(args["template"], INSPECT_TEMPLATE_TOOL, context);
3409
+ const budget = budgetOf();
3410
+ if (template.format === "xlsx") {
3411
+ const skeleton = await xlsx.read(template.bytes);
3412
+ const only = typeof args["sheet"] === "string" ? args["sheet"] : void 0;
3413
+ const sheets = only === void 0 ? skeleton.sheets : skeleton.sheets.filter((sheet) => sheet.name === only);
3414
+ if (only !== void 0 && sheets.length === 0) throw new WebSkillError("TEMPLATE_TARGET_MISSING", `The workbook has no sheet named "${only}". It has: ${skeleton.sheets.map((s) => s.name).join(", ")}.`);
3415
+ let left = budget.cells;
3416
+ let truncated = false;
3417
+ const described = sheets.map((sheet) => {
3418
+ const cells = sheet.cells.slice(0, Math.max(left, 0));
3419
+ if (cells.length < sheet.cells.length) truncated = true;
3420
+ left -= cells.length;
3421
+ return {
3422
+ name: sheet.name,
3423
+ range: sheet.range,
3424
+ cells: cells.map((cell) => ({
3425
+ address: cell.address,
3426
+ text: clip(cell.text, budget.chars),
3427
+ ...cell.formula === void 0 ? {} : { formula: cell.formula }
3428
+ })),
3429
+ ...sheet.merges.length > 0 ? { merges: [...sheet.merges] } : {},
3430
+ ...cells.length < sheet.cells.length ? { omittedCells: sheet.cells.length - cells.length } : {}
3431
+ };
3432
+ });
3433
+ return json({
3434
+ file: template.name,
3435
+ format: "xlsx",
3436
+ sheets: described,
3437
+ ...truncated ? { truncated: `Only the first ${budget.cells} filled cells are listed; inspect one sheet at a time.` } : {}
3438
+ });
3439
+ }
3440
+ const skeleton = await docx.read(template.bytes);
3441
+ let left = budget.cells;
3442
+ const paragraphs = skeleton.paragraphs.filter((paragraph) => paragraph.text !== "").slice(0, left);
3443
+ left -= paragraphs.length;
3444
+ const cells = skeleton.cells.slice(0, Math.max(left, 0));
3445
+ const tableCount = skeleton.cells.reduce((max, cell) => Math.max(max, cell.table + 1), 0);
3446
+ const omitted = skeleton.cells.length - cells.length;
3447
+ return json({
3448
+ file: template.name,
3449
+ format: "docx",
3450
+ paragraphs: paragraphs.map((paragraph) => ({
3451
+ index: paragraph.index,
3452
+ text: clip(paragraph.text, budget.chars)
3453
+ })),
3454
+ tableCount,
3455
+ cells: cells.map((cell) => ({
3456
+ table: cell.table,
3457
+ row: cell.row,
3458
+ column: cell.column,
3459
+ text: clip(cell.text, budget.chars),
3460
+ ...cell.gridSpan === void 0 ? {} : { gridSpan: cell.gridSpan },
3461
+ ...cell.vMerge === void 0 ? {} : { vMerge: cell.vMerge }
3462
+ })),
3463
+ ...omitted > 0 ? { truncated: `${omitted} further table cell(s) are not listed; the template is larger than the budget.` } : {}
3464
+ });
3465
+ }
3466
+ async function fill(args, context) {
3467
+ if (writeArtifact === void 0) throw new WebSkillError("TOOL_UNSUPPORTED", `${FILL_TEMPLATE_TOOL} is not available in this host.`);
3468
+ const template = await load(args["template"], FILL_TEMPLATE_TOOL, context);
3469
+ const fileName = resolveFileName(args["fileName"], template.format);
3470
+ const images = await resolveImages(args["images"], context);
3471
+ if (template.format === "xlsx") {
3472
+ const spec = {
3473
+ ...Array.isArray(args["rows"]) ? { rows: args["rows"] } : {},
3474
+ ...Array.isArray(args["cells"]) ? { cells: args["cells"] } : {},
3475
+ ...images.length > 0 ? { images: images.map((image) => {
3476
+ const address = image.placement["address"];
3477
+ if (typeof address !== "string" || address === "") throw new WebSkillError("TEMPLATE_ADDRESS_INVALID", `The picture "${image.ref}" needs an "address" telling which cell it goes on.`);
3478
+ const sheet = image.placement["sheet"];
3479
+ return {
3480
+ address,
3481
+ ...typeof sheet === "string" ? { sheet } : {},
3482
+ ...image.bytes
3483
+ };
3484
+ }) } : {}
3485
+ };
3486
+ const bytes = await xlsx.fill(template.bytes, spec);
3487
+ const artifact = await writeArtifact(fileName, bytes, XLSX_MIME, context);
3488
+ return json({
3489
+ path: artifact.path,
3490
+ size: artifact.size,
3491
+ filledCount: spec.cells?.length ?? 0,
3492
+ insertedRows: (spec.rows ?? []).reduce((total, row) => total + row.count, 0),
3493
+ ...images.length > 0 ? { imageCount: images.length } : {}
3494
+ });
3495
+ }
3496
+ const spec = {
3497
+ ...Array.isArray(args["texts"]) ? { texts: args["texts"] } : {},
3498
+ ...images.length > 0 ? { images: images.map((image) => ({
3499
+ ...pickCoordinates(image.placement),
3500
+ ...image.bytes
3501
+ })) } : {}
3502
+ };
3503
+ const bytes = await docx.fill(template.bytes, spec);
3504
+ const artifact = await writeArtifact(fileName, bytes, DOCX_MIME, context);
3505
+ return json({
3506
+ path: artifact.path,
3507
+ size: artifact.size,
3508
+ filledCount: spec.texts?.length ?? 0,
3509
+ ...images.length > 0 ? { imageCount: images.length } : {}
3510
+ });
3511
+ }
3512
+ async function resolveImages(raw, context) {
3513
+ if (!Array.isArray(raw) || raw.length === 0) return [];
3514
+ if (resolveImage === void 0) throw new WebSkillError("TEMPLATE_IMAGE_UNRESOLVED", "This host cannot resolve picture references.");
3515
+ const out = [];
3516
+ for (const entry of raw) {
3517
+ if (!isRecord(entry) || typeof entry["ref"] !== "string") throw new WebSkillError("TEMPLATE_IMAGE_UNRESOLVED", "Every picture needs a \"ref\".");
3518
+ const ref = entry["ref"];
3519
+ const bytes = await resolveImage(ref, context);
3520
+ if (bytes === void 0) throw new WebSkillError("TEMPLATE_IMAGE_UNRESOLVED", `Nothing in this conversation matches the picture reference "${ref}".`);
3521
+ out.push({
3522
+ ref,
3523
+ bytes,
3524
+ placement: entry
3525
+ });
3526
+ }
3527
+ return out;
3528
+ }
3529
+ }
3530
+ function pickCoordinates(placement) {
3531
+ const out = {};
3532
+ for (const key of [
3533
+ "paragraph",
3534
+ "table",
3535
+ "row",
3536
+ "column"
3537
+ ]) {
3538
+ const value = placement[key];
3539
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0) out[key] = value;
3540
+ }
3541
+ return out;
3542
+ }
3543
+ /** 后缀必须与模板同格式:拿 xlsx 模板出 .docx 是意图冲突,不是笔误 */
3544
+ function resolveFileName(value, format) {
3545
+ if (typeof value !== "string" || value.trim() === "") throw new WebSkillError("VALIDATION_FAILED", `${FILL_TEMPLATE_TOOL} requires a non-empty "fileName".`);
3546
+ const fileName = value.trim();
3547
+ const lastSegment = fileName.split(/[\\/]/).pop() ?? "";
3548
+ const dot = lastSegment.lastIndexOf(".");
3549
+ const extension = dot <= 0 ? "" : lastSegment.slice(dot + 1).toLowerCase();
3550
+ if (extension === "") return `${fileName}.${format}`;
3551
+ if (extension === format) return fileName;
3552
+ throw new WebSkillError("VALIDATION_FAILED", `The template is a .${format} file, so the result is a .${format} file; "${fileName}" asks for .${extension}.`);
3553
+ }
3554
+ function clip(text, limit) {
3555
+ const max = Math.max(Math.floor(limit / 40), 40);
3556
+ return text.length <= max ? text : `${text.slice(0, max)}…`;
3557
+ }
3558
+ function json(data) {
3559
+ return {
3560
+ ok: true,
3561
+ content: [{
3562
+ type: "json",
3563
+ data
3564
+ }]
3565
+ };
3566
+ }
3567
+
3568
+ //#endregion
3569
+ //#region ../agent/src/pageImage/toolSource.ts
3570
+ const CAPTURE_PAGE_IMAGE_TOOL = "capture_page_image";
3571
+ const DESCRIPTION$1 = "Save a picture that is already on the page as a run artifact, so a skill can place it in a document.";
3572
+ const INPUT_SCHEMA$1 = {
3573
+ type: "object",
3574
+ properties: {
3575
+ ref: {
3576
+ type: "string",
3577
+ description: "Element handle from perceive_page. Only nodes marked \"capturable\" can be captured — those are the <img>, <canvas> and <svg> elements."
3578
+ },
3579
+ note: {
3580
+ type: "string",
3581
+ description: "Why you need this picture. It is shown to the user when asking for permission."
3582
+ }
3583
+ },
3584
+ required: ["ref"],
3585
+ additionalProperties: false
3586
+ };
3587
+ const SYSTEM_PROMPT$2 = "Use capture_page_image when a picture on the current page belongs in a document you are about to produce. It returns a reference, never the picture itself — pass that reference to the skill, do not try to read it back. Capturing does not tell you what the picture shows; ask for a page perception with images if you need that. Capture one picture per call, and only for nodes the page perception marked as capturable. Do not list the captured references back to the user. They are working material for the document, not a deliverable; say how many pictures went in and leave it at that.";
3588
+ const isFailure$1 = (result) => "reason" in result;
3589
+ /**
3590
+ * 页面取像工具(0.22.0 分册 12)。
3591
+ *
3592
+ * 与 `export_spreadsheet` 同一套 `ExternalToolSource` 范式(FR-12.1),
3593
+ * 不另造第二套注册机制。
3594
+ * @experimental
3595
+ */
3596
+ function createPageImageToolSource(options) {
3597
+ const { capture } = options;
3598
+ return {
3599
+ kind: "page-image",
3600
+ listToolSpecs: () => Promise.resolve(capture ? [{
3601
+ name: CAPTURE_PAGE_IMAGE_TOOL,
3602
+ description: DESCRIPTION$1,
3603
+ inputSchema: INPUT_SCHEMA$1
3604
+ }] : []),
3605
+ systemPrompt: () => Promise.resolve(capture ? SYSTEM_PROMPT$2 : void 0),
3606
+ canHandle: (name) => name === CAPTURE_PAGE_IMAGE_TOOL,
3607
+ captureArgs: (_name, args) => typeof args["ref"] === "string" ? { ref: args["ref"] } : {},
3608
+ call: async (name, args, context) => {
3609
+ try {
3610
+ if (name !== "capture_page_image" || !capture) throw new WebSkillError("TOOL_UNSUPPORTED", `${name} is not available in this host.`);
3611
+ const ref = args["ref"];
3612
+ if (typeof ref !== "string" || ref.trim() === "") throw new WebSkillError("VALIDATION_FAILED", `${CAPTURE_PAGE_IMAGE_TOOL} requires a non-empty "ref" taken from a page perception.`);
3613
+ const note = typeof args["note"] === "string" && args["note"].trim() !== "" ? args["note"].trim() : void 0;
3614
+ const result = await capture({
3615
+ ref: ref.trim(),
3616
+ ...note === void 0 ? {} : { note }
3617
+ }, context);
3618
+ if (isFailure$1(result)) {
3619
+ const levels = result.triedLevels ?? [];
3620
+ return {
3621
+ ok: false,
3622
+ content: [],
3623
+ error: {
3624
+ code: "TOOL_EXECUTION_FAILED",
3625
+ message: levels.length > 0 ? `${result.reason} (tried: ${levels.join(", ")})` : result.reason
3626
+ }
3627
+ };
3628
+ }
3629
+ return {
3630
+ ok: true,
3631
+ content: [{
3632
+ type: "json",
3633
+ data: {
3634
+ ref: ref.trim(),
3635
+ artifact: result.artifact,
3636
+ width: result.width,
3637
+ height: result.height,
3638
+ bytes: result.bytes
3639
+ }
3640
+ }]
3641
+ };
3642
+ } catch (e) {
3643
+ return {
3644
+ ok: false,
3645
+ content: [],
3646
+ error: {
3647
+ code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
3648
+ message: messageOf(e)
3649
+ }
3650
+ };
3651
+ }
3652
+ }
3653
+ };
3654
+ }
3655
+
3656
+ //#endregion
3657
+ //#region ../agent/src/remoteImage/toolSource.ts
3658
+ const FETCH_REMOTE_IMAGE_TOOL = "fetch_remote_image";
3659
+ const DESCRIPTION = "Download an image from a public URL and save it as a run artifact, so a skill can place it in a document.";
3660
+ const INPUT_SCHEMA = {
3661
+ type: "object",
3662
+ properties: {
3663
+ url: {
3664
+ type: "string",
3665
+ description: "Absolute https:// or http:// address of the image. Addresses on private networks are refused."
3666
+ },
3667
+ note: {
3668
+ type: "string",
3669
+ description: "Why you need this image. It is shown to the user when asking for permission."
3670
+ },
3671
+ describe: {
3672
+ type: "boolean",
3673
+ description: "Attach the image itself to the result so you can look at it. Set it only when the user asked what the picture shows; leave it out when you are collecting images for a document."
3674
+ }
3675
+ },
3676
+ required: ["url"],
3677
+ additionalProperties: false
3678
+ };
3679
+ const SYSTEM_PROMPT$1 = "Use fetch_remote_image when a document you are about to produce needs an image that lives at a public URL. To just show a public image in the conversation, write plain markdown — ![alt](https://…) — and do not call this tool. It returns a reference to pass on to the skill: a reference is not a path and cannot be read back. Set \"describe\" to true only when the user asked what a picture shows — it attaches the image so you can look at it, which costs extra, so leave it out while you are only collecting images for a document. Without \"describe\" you have not seen the image: say you cannot see it rather than guessing. Never infer the subject from the URL, the file name or the site it came from. The user is asked for permission before the first download, because fetching reveals their IP address to that site. Fetch one image per call, and only from addresses you got from the page or from the user — never guess a URL. Do not list the fetched references back to the user; say how many images went into the document instead.";
3680
+ const isFailure = (result) => "reason" in result;
3681
+ /**
3682
+ * 外链图片抓取工具(0.22.0 分册 13)。
3683
+ *
3684
+ * 与 `capture_page_image` 同一套范式:模型给出来源、宿主拿字节、回给模型的只有引用。
3685
+ * 三种图片来源(上传、页面、外链)因此在技能那一侧长得一模一样(AC-G1)。
3686
+ * @experimental
3687
+ */
3688
+ function createRemoteImageToolSource(options) {
3689
+ const { fetchImage } = options;
3690
+ return {
3691
+ kind: "remote-image",
3692
+ listToolSpecs: () => Promise.resolve(fetchImage ? [{
3693
+ name: FETCH_REMOTE_IMAGE_TOOL,
3694
+ description: DESCRIPTION,
3695
+ inputSchema: INPUT_SCHEMA
3696
+ }] : []),
3697
+ systemPrompt: () => Promise.resolve(fetchImage ? SYSTEM_PROMPT$1 : void 0),
3698
+ canHandle: (name) => name === FETCH_REMOTE_IMAGE_TOOL,
3699
+ captureArgs: (_name, args) => typeof args["url"] === "string" ? { url: args["url"] } : {},
3700
+ call: async (name, args, context) => {
3701
+ try {
3702
+ if (name !== "fetch_remote_image" || !fetchImage) throw new WebSkillError("TOOL_UNSUPPORTED", `${name} is not available in this host.`);
3703
+ const url = args["url"];
3704
+ if (typeof url !== "string" || url.trim() === "") throw new WebSkillError("VALIDATION_FAILED", `${FETCH_REMOTE_IMAGE_TOOL} requires a non-empty "url" pointing at an image.`);
3705
+ const note = typeof args["note"] === "string" && args["note"].trim() !== "" ? args["note"].trim() : void 0;
3706
+ const describe = args["describe"] === true;
3707
+ const result = await fetchImage({
3708
+ url: url.trim(),
3709
+ ...note === void 0 ? {} : { note },
3710
+ ...describe ? { describe } : {}
3711
+ }, context);
3712
+ if (isFailure(result)) return {
3713
+ ok: false,
3714
+ content: [],
3715
+ error: {
3716
+ code: "TOOL_EXECUTION_FAILED",
3717
+ message: result.kind === void 0 ? result.reason : `${result.reason} [${result.kind}]`
3718
+ }
3719
+ };
3720
+ const data = describe ? result.data : void 0;
3721
+ const mimeType = describe ? result.mimeType : void 0;
3722
+ const seen = data !== void 0 && mimeType !== void 0;
3723
+ const content = [{
3724
+ type: "json",
3725
+ data: {
3726
+ url: url.trim(),
3727
+ artifact: result.artifact,
3728
+ width: result.width,
3729
+ height: result.height,
3730
+ bytes: result.bytes,
3731
+ ...seen ? { note: "The image in the next message is the picture downloaded from this URL." } : {}
3732
+ }
3733
+ }];
3734
+ if (data !== void 0 && mimeType !== void 0) content.push({
3735
+ type: "image",
3736
+ id: result.artifact.replace(/^artifact:/, ""),
3737
+ mimeType,
3738
+ data
3739
+ });
3740
+ return {
3741
+ ok: true,
3742
+ content
3743
+ };
3744
+ } catch (e) {
3745
+ return {
3746
+ ok: false,
3747
+ content: [],
3748
+ error: {
3749
+ code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
3750
+ message: messageOf(e)
3751
+ }
3752
+ };
3753
+ }
3754
+ }
3755
+ };
3756
+ }
3757
+
3758
+ //#endregion
3759
+ //#region ../agent/src/dwg/views.ts
3760
+ /**
3761
+ * 视图取用(0.22.0 分册 23 FR-23.5 / FR-23.7)。
3762
+ *
3763
+ * 单独成文件是因为 diff、导出、投放面三边都要用同一套「哪些视图、按什么配对」,
3764
+ * 各写一份迟早会在某一边把空布局漏掉。
3765
+ */
3766
+ /** 模型空间视图的固定 id。两份图纸靠它配对 */
3767
+ const DWG_MODEL_VIEW_ID = "Model";
3768
+ /**
3769
+ * 文档的视图列表。
3770
+ *
3771
+ * 分册 23 之前解析出的文档没有 `views`,给它补一个模型空间视图:
3772
+ * 那时的 `entities` 本来就是模型空间的全部内容,语义对得上。
3773
+ */
3774
+ function dwgViewsOf(doc) {
3775
+ const views = doc.views;
3776
+ if (views && views.length > 0) return views;
3777
+ return [{
3778
+ id: DWG_MODEL_VIEW_ID,
3779
+ name: DWG_MODEL_VIEW_ID,
3780
+ kind: "model",
3781
+ entities: doc.entities,
3782
+ bounds: doc.bounds,
3783
+ viewports: []
3784
+ }];
3785
+ }
3786
+ /** 按 id 取视图;取不到就退回第一个,而不是什么都没有 */
3787
+ function dwgPickView(doc, viewId) {
3788
+ const views = dwgViewsOf(doc);
3789
+ return views.find((v) => v.id === viewId) ?? views[0];
3790
+ }
3791
+
3792
+ //#endregion
3793
+ //#region ../agent/src/dwg/toolSource.ts
3794
+ const VIEW_DWG_TOOL = "view_dwg";
3795
+ /** 图层汇总进上下文的条数上限:图纸动辄几十上百个图层,全给等于把预算烧在噪声上 */
3796
+ const MAX_LAYER_ROWS = 25;
3797
+ /** 叠加的硬上限(FR-35.1)。三份叠在一起没有颜色方案能让人分得清 */
3798
+ const MAX_DRAWINGS = 2;
3799
+ const VIEW_SCHEMA = {
3800
+ type: "object",
3801
+ properties: { files: {
3802
+ type: "array",
3803
+ items: {
3804
+ type: "string",
3805
+ description: "The id of a .dwg. It can come from the download list, from a file the user attached, or from the id that read_linked_document returns for a drawing on the page. The download listing marks .dwg as unreadable because it has no text to extract — pass the id here anyway."
3806
+ },
3807
+ minItems: 1,
3808
+ maxItems: MAX_DRAWINGS,
3809
+ description: "One drawing id, or two to stack them on top of each other. Two drawings are overlaid in the same coordinate system so the user can see where they differ and measure it — the first id becomes the top layer. Pass two only when the user asked to look at two drawings."
3810
+ } },
3811
+ required: ["files"],
3812
+ additionalProperties: false
3813
+ };
3814
+ const SYSTEM_PROMPT = "Use view_dwg to show a CAD drawing. Drawings reach you as ids, never as content: from the download list, from a file the user attached, or from read_linked_document when the drawing is linked on the page. Those three all hand you an id and nothing else — a drawing is far too large to put in front of you, so an id with no readable content is the expected outcome, not a failed read. list_downloaded_files reports .dwg as unreadable — that verdict is about extracting text, and does not apply here: hand the id to this tool and it will read the drawing itself. It opens a viewer for the user and returns only a summary to you — the drawing geometry never comes back as data, so do not ask for entity coordinates and do not try to describe the drawing from the tool result alone. The user drives the viewer: they switch between sheets and model space, turn layers on and off, and measure distances, areas and angles there. Pass two ids to stack two drawings in one viewer. That is for the user to look at, not for you to compare: you never receive the geometry, so you cannot say what changed between them. Tell the user to look and to measure, and offer to explain what the viewer controls do. The \"approximated\" count is geometry drawn imprecisely and the \"notDrawn\" count is geometry missing from the viewer entirely — say which one applies rather than merging them.";
3815
+ function omissionNote(doc) {
3816
+ return doc.omissions.map((o) => {
3817
+ switch (o.reason) {
3818
+ case "acis-not-tessellated": return `${o.count} ${o.kind} solid(s) cannot be displayed: they are ACIS solids, which this reader does not tessellate.`;
3819
+ case "pattern-approximated": return `${o.count} ${o.kind} entity(ies) are drawn with an approximated fill pattern, not the exact hatch definition.`;
3820
+ case "pattern-too-dense": return `${o.count} hatch(es) have a pattern too dense to draw; their outline is shown without the fill.`;
3821
+ case "linetype-unavailable": return `${o.count} entity(ies) reference a line type whose dash definition is unavailable; they are drawn solid.`;
3822
+ case "unsupported-entity": return `${o.count} ${o.kind} entity(ies) are not supported and were not drawn.`;
3823
+ }
3824
+ });
3825
+ }
3826
+ function summariseDocument(doc) {
3827
+ const views = dwgViewsOf(doc);
3828
+ const tally = (severity) => doc.omissions.filter((o) => o.severity === severity).reduce((total, o) => total + o.count, 0);
3829
+ return {
3830
+ version: doc.version,
3831
+ entities: views.reduce((total, v) => total + v.entities.length, 0),
3832
+ views: views.map((v) => ({
3833
+ id: v.id,
3834
+ kind: v.kind,
3835
+ entities: v.entities.length,
3836
+ viewports: v.viewports.length
3837
+ })),
3838
+ layers: doc.layers.map((l) => l.name).slice(0, MAX_LAYER_ROWS),
3839
+ layerCount: doc.layers.length,
3840
+ approximated: tally("degraded"),
3841
+ notDrawn: tally("dropped"),
3842
+ notes: omissionNote(doc)
3843
+ };
3844
+ }
3845
+ /**
3846
+ * 取件号列表(FR-35.1 / AC-35.2)。
3847
+ *
3848
+ * 超过上限**整调用拒绝**,不截断:模型传了三份却只看到两份,
3849
+ * 它会把缺的那份归因到图纸而不是工具。
3850
+ */
3851
+ function requireFiles(args) {
3852
+ const raw = args["files"];
3853
+ if (!Array.isArray(raw) || raw.length === 0) throw new WebSkillError("VALIDATION_FAILED", `${VIEW_DWG_TOOL} requires a non-empty "files" array.`);
3854
+ if (raw.length > MAX_DRAWINGS) throw new WebSkillError("VALIDATION_FAILED", `${VIEW_DWG_TOOL} opens at most ${MAX_DRAWINGS} drawings at once; ${raw.length} were requested. Ask the user which two to look at.`);
3855
+ return raw.map((value, index) => {
3856
+ if (typeof value !== "string" || value.trim() === "") throw new WebSkillError("VALIDATION_FAILED", `${VIEW_DWG_TOOL} "files[${index}]" must be a non-empty string.`);
3857
+ return value.trim();
3858
+ });
3859
+ }
3860
+ /** 宽松判断:只要带协议头就按链接处理。宁可多脱敏一次,也不要漏掉一个带令牌的地址 */
3861
+ function looksLikeUrl(value) {
3862
+ return /^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim());
3863
+ }
3864
+ /** 把 `load()` 的两种返回形态归一(FR-31.1)。判据是有没有 `document`,不是 `in` 关键字之外的猜测 */
3865
+ async function readDrawing(load, file, context) {
3866
+ const loaded = await load(file, context);
3867
+ if (!("document" in loaded)) return { document: loaded };
3868
+ const name = typeof loaded.name === "string" ? loaded.name.trim() : "";
3869
+ return {
3870
+ document: loaded.document,
3871
+ ...name === "" ? {} : { name }
3872
+ };
3873
+ }
3874
+ /**
3875
+ * DWG 查看工具(0.22.0 分册 19,分册 34 收敛为单工具)。
3876
+ *
3877
+ * 与 `capture_page_image` 同一套 `ExternalToolSource` 范式。
3878
+ * 解析与投放都由宿主注入:本包不认识 `.dwg` 字节,也不认识 `documentSurface`。
3879
+ * @experimental
3880
+ */
3881
+ function createDwgToolSource(options) {
3882
+ const { load, project } = options;
3883
+ const available = load !== void 0 && project !== void 0;
3884
+ return {
3885
+ kind: "dwg",
3886
+ listToolSpecs: () => Promise.resolve(available ? [{
3887
+ name: VIEW_DWG_TOOL,
3888
+ description: "Open a CAD drawing (DWG) in a viewer.",
3889
+ inputSchema: VIEW_SCHEMA
3890
+ }] : []),
3891
+ systemPrompt: () => Promise.resolve(available ? SYSTEM_PROMPT : void 0),
3892
+ canHandle: (name) => name === VIEW_DWG_TOOL,
3893
+ /**
3894
+ * 已审查(分册 30):文件引用**通常**是用户可见的名字。
3895
+ * 但 schema 允许传页面上的链接,而链接可能是带签名令牌的临时地址——
3896
+ * 那种东西进技能快照就是把凭证写进一份可分享的产物。
3897
+ * 所以不是把字段永久标成敏感,而是本次引用长得像 URL 时整调用不留。
3898
+ */
3899
+ argCaptureTrust: (name, args) => {
3900
+ if (name !== "view_dwg") return void 0;
3901
+ const files = args["files"];
3902
+ return Array.isArray(files) && files.some((f) => typeof f === "string" && looksLikeUrl(f)) ? { tier: "untrusted" } : { tier: "reviewed" };
3903
+ },
3904
+ captureArgs: (_name, args) => {
3905
+ const files = args["files"];
3906
+ return Array.isArray(files) ? { files: files.filter((f) => typeof f === "string") } : {};
3907
+ },
3908
+ call: async (name, args, context) => {
3909
+ try {
3910
+ if (!available || name !== "view_dwg") throw new WebSkillError("TOOL_UNSUPPORTED", `${name} is not available in this host.`);
3911
+ const files = requireFiles(args);
3912
+ const drawings = [];
3913
+ for (const file of files) {
3914
+ const drawing = await readDrawing(load, file, context);
3915
+ drawings.push({
3916
+ title: file,
3917
+ document: drawing.document,
3918
+ ...drawing.name === void 0 ? {} : { fileName: drawing.name }
3919
+ });
3920
+ }
3921
+ await project({ drawings }, context);
3922
+ return {
3923
+ ok: true,
3924
+ content: [{
3925
+ type: "json",
3926
+ data: { drawings: drawings.map((d) => ({
3927
+ file: d.title,
3928
+ ...d.fileName === void 0 ? {} : { fileName: d.fileName },
3929
+ ...summariseDocument(d.document)
3930
+ })) }
3931
+ }]
3932
+ };
3933
+ } catch (e) {
3934
+ return {
3935
+ ok: false,
3936
+ content: [],
3937
+ error: {
3938
+ code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
3939
+ message: messageOf(e)
3940
+ }
3941
+ };
3942
+ }
3943
+ }
3944
+ };
3945
+ }
3946
+
3947
+ //#endregion
3948
+ export { CAPTURE_PAGE_IMAGE_TOOL, CLOSE_PAGE_TAB_TOOL, DEFAULT_MAX_DATA_SOURCE_BYTES, DELEGATE_TASK_TOOL, DELEGATION_SYSTEM_PROMPT, DOCUMENT_READ_BUDGET, DOWNLOAD_WAIT_DEFAULT_MS, DOWNLOAD_WAIT_MAX_MS, DOWNLOAD_WAIT_MIN_MS, DWG_MODEL_VIEW_ID, DataSourceCandidatePolicy, DataSourcePolicy, DelegationOrchestrator, DownloadedFilePolicy, EMU_PER_PIXEL, EMU_PER_POINT, EMU_PER_TWIP, EXPORT_SPREADSHEET_TOOL, FETCH_REMOTE_IMAGE_TOOL, FILL_TEMPLATE_TOOL, FOCUS_PAGE_TAB_TOOL, GENERATE_SKILL_TOOL, INSPECT_TEMPLATE_TOOL, LIST_DOWNLOADED_FILES_TOOL, LIST_PAGE_TABS_TOOL, MANAGE_TODO_TOOL, MAX_DIGIT_WIDTH, PAGE_ACTION_KINDS, PAGE_ACTION_SYSTEM_PROMPT, PAGE_ACTION_TOOL, PAGE_DOCUMENT_REF, PDF_GARBLED_RATIO, PDF_LARGE_IMAGE_RATIO, PERCEIVE_PAGE_TOOL, PERCEPTION_PAGING_DEFAULTS, PERCEPTION_SYSTEM_PROMPT, PageActionPolicy, PagePerceptionPolicy, READ_DOCUMENT_TOOL, READ_DOWNLOADED_FILE_TOOL, SKILL_GENERATION_SYSTEM_PROMPT, SkillGenerator, TAB_WORKSET_DEFAULT_MAX, TAB_WORKSET_MAX_MAX, TAB_WORKSET_MIN_MAX, TAB_WORKSET_SYSTEM_PROMPT, TEMPLATE_INSPECT_BUDGET, TODO_SYSTEM_PROMPT, TabWorksetPolicy, TodoStore, VIEW_DWG_TOOL, asReadMode, clampDownloadWaitMs, clampWorksetMax, columnWidthToPixels, createDelegationToolSource, createDocumentToolSource, createDownloadedFileToolSource, createDwgToolSource, createHttpDataSourceTransport, createPageActionToolSource, createPageImageToolSource, createPagePerceptionToolSource, createRemoteImageToolSource, createSkillGenerationToolSource, createSpreadsheetToolSource, createTabWorksetToolSource, createTemplateToolSource, createTodoToolSource, defersImage, dwgPickView, dwgViewsOf, fitWithin, formatCellAddress, frameLabel, frameSteps, isPdfTextTrustworthy, isRangeTopLeft, measurePdfTextQuality, parseCellAddress, parseCellRange, rangeContains, readImageSize, sniffDocumentFormat, takesImage, toActionFrameScopes, toFrameScopes, withDelegationOrigin };