@webskill/sdk 0.27.1 → 0.28.1

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.
@@ -5,7 +5,7 @@ import { t as assertRemoteUrlAllowed } from "./urlSafety-CiSuCJvX.js";
5
5
  import { c as findUnpairedToolCalls, d as partsToText, f as promptText, l as interruptedToolResult, p as textParts, u as sealToolCallPairs } from "./llm-eIQNO9tr.js";
6
6
  import { a as UNSUPPORTED_DOCUMENT_MESSAGE, c as toBase64, f as SPREADSHEET_MIME_TYPE, g as DEFAULT_MAX_DOCUMENT_TEXT_BYTES, h as DEFAULT_MAX_DOCUMENT_BYTES, i as SUPPORTED_DOCUMENT_MIME, l as encodeSpreadsheet, n as READ_LINKED_DOCUMENT_TOOL, o as fromBase64, r as READ_LINKED_DOCUMENT_TOOL_NAME, s as isBytesOnlyDocument, v as TEXT_BUDGETED_CONTENT_TYPES } from "./linkedDocument-0iiVPd5t.js";
7
7
  import { t as mergeCatalogEntries } from "./external-_ZRQe-V9.js";
8
- import { i as isArtifactCardHidden, n as extractChartSpec, r as admitArtifactMetadata, t as buildRenderResult } from "./renderResult-D9Q-Vu2x.js";
8
+ import { i as isArtifactCardHidden, n as extractChartSpec, r as admitArtifactMetadata, t as buildRenderResult } from "./renderResult-2v3WQzs_.js";
9
9
  import { t as extractUiSpecEvents } from "./surface-DVGiCmwq.js";
10
10
  import { t as MemoryArtifactStore } from "./memoryArtifactStore-Bs7glPqm.js";
11
11
 
package/dist/browser.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { G as FileStat, It as VerifyResult, K as FileSystemProvider, Lt as WebSkillError, N as ArchiveLimits, Ot as SkillsLockfile, Pt as UnsignedPolicy, S as UiBridge, St as SkillManifest, b as RenderResultRequest, c as InteractionResponse, jt as TrustedKeyStore, l as LlmClient, m as LlmStreamEvent, o as InteractionPolicy, p as LlmResponse, q as FileWriteStream, s as InteractionRequest, u as LlmCompleteInput, v as MemoryStore, vt as SkillInstallSource } from "./types-CpDRZ0rA-CFQ-vdKz.js";
2
- import { $ as ImageOmission, B as ExternalToolSource, C as BridgeResponse, N as DocumentSurfaceHost, Pn as ToolResult, S as BridgeRequest, Sr as bridgeError, Yr as parseBridgeRequest, b as BridgeCapabilities, ct as LinkedDocumentReader, en as ScriptExecutionContext, jn as ToolDefinition, m as ApprovalScope, mr as WebSkillApi, sr as ViewerImageFact, tn as ScriptExecutor, ut as NetworkPolicy, z as ExternalSkillProvider } from "./index-D6eSrEi5.js";
3
- import { $r as XlsxTableReader, In as PerceptionScopeInput, K as DocxDocumentContent, Ln as PptxDocumentContent, Pn as PerceptionResult, Rn as PptxDocumentReader, Tn as PdfDocumentReader, Ut as NestedFrameRef, Xr as XlsxDocumentReader, Yr as XlsxDocumentContent, cn as PageActionRequest, dn as PageActionTarget, fr as SubtreeRejection, in as PageActionOutcome, jn as PerceptionCaptureOptions, kn as PerceivedNode, mn as PageAuditSink, nn as PageActionExecutor, q as DocxDocumentReader, u as ConsentUi, un as PageActionScopeInput, xn as PagePerceptionReader, yn as PagePerceptionPolicy } from "./index--GvzV6Lq.js";
1
+ import { G as FileStat, It as VerifyResult, K as FileSystemProvider, Lt as WebSkillError, N as ArchiveLimits, Ot as SkillsLockfile, Pt as UnsignedPolicy, S as UiBridge, St as SkillManifest, b as RenderResultRequest, c as InteractionResponse, jt as TrustedKeyStore, l as LlmClient, m as LlmStreamEvent, o as InteractionPolicy, p as LlmResponse, q as FileWriteStream, s as InteractionRequest, u as LlmCompleteInput, v as MemoryStore, vt as SkillInstallSource } from "./types-wKp3YBSM-167azx1H.js";
2
+ import { $ as ImageOmission, B as ExternalToolSource, C as BridgeResponse, N as DocumentSurfaceHost, Pn as ToolResult, S as BridgeRequest, Sr as bridgeError, Yr as parseBridgeRequest, b as BridgeCapabilities, ct as LinkedDocumentReader, en as ScriptExecutionContext, jn as ToolDefinition, m as ApprovalScope, mr as WebSkillApi, sr as ViewerImageFact, tn as ScriptExecutor, ut as NetworkPolicy, z as ExternalSkillProvider } from "./index-DU8sKyQq.js";
3
+ import { In as PerceptionScopeInput, K as DocxDocumentContent, Ln as PptxDocumentContent, Pn as PerceptionResult, Rn as PptxDocumentReader, Tn as PdfDocumentReader, Ut as NestedFrameRef, cn as PageActionRequest, dn as PageActionTarget, ii as XlsxDocumentReader, in as PageActionOutcome, jn as PerceptionCaptureOptions, kn as PerceivedNode, mn as PageAuditSink, nn as PageActionExecutor, q as DocxDocumentReader, ri as XlsxDocumentContent, si as XlsxTableReader, u as ConsentUi, un as PageActionScopeInput, xn as PagePerceptionReader, yn as PagePerceptionPolicy, yr as SubtreeRejection } from "./index-CkauSvus.js";
4
4
  //#region ../browser/dist/index.d.ts
5
5
  //#region src/fs/featureDetection.d.ts
6
6
  /** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
@@ -18,6 +18,7 @@ declare class OpfsProvider implements FileSystemProvider {
18
18
  writeText(p: string, content: string): Promise<void>;
19
19
  appendText(p: string, content: string): Promise<void>;
20
20
  readBinary(p: string): Promise<Uint8Array>;
21
+ createReadStream(p: string): Promise<AsyncIterable<Uint8Array>>;
21
22
  writeBinary(p: string, content: Uint8Array): Promise<void>;
22
23
  /**
23
24
  * 分块写入(FR-18.2c)。`createWritable()` 本身就是写 swap 文件、`close()` 才提交,
@@ -1650,11 +1651,10 @@ declare function readXlsxBlocks(bytes: Uint8Array): Promise<XlsxDocumentContent>
1650
1651
  //#endregion
1651
1652
  //#region src/document/xlsxTable.d.ts
1652
1653
  /**
1653
- * xlsx → 单元格网格(0.26.0 分册 10 · FR-10.20)。
1654
+ * xlsx → 单元格网格或可重复扫描的行流。
1654
1655
  *
1655
- * 与 `extractXlsxText` 共用 `readXlsxWorkbook` 的同一份解析(单元格取值、日期换算、空格补位),
1656
- * 所以画像与查询看到的格,和模型从文本里读到的格是同一批。
1657
- * 错误语义同 `extractXlsxText`:旧版 `.xls`、归档损坏、缺 `xl/workbook.xml` 都抛 `WebSkillError`。
1656
+ * `read` 保留既有网格契约;`readStreaming` 用 SAX 逐行解析工作表,不受整包解压的 64 MiB 条目上限约束。
1657
+ * 两条路径使用同一套单元格取值、日期换算与空格补位规则,错误均带 `WebSkillError`。
1658
1658
  * @experimental
1659
1659
  */
1660
1660
  declare function createXlsxTableReader(): XlsxTableReader;
package/dist/browser.js CHANGED
@@ -3,13 +3,14 @@ import { A as SKILL_MANIFEST_FILE, D as verifySkillSignature, M as buildManifest
3
3
  import { o as atomicWriteText, r as resolveInsideRoot, s as isAtomicTempPath } from "./pathSecurity-CirkQwWP.js";
4
4
  import { t as assertRemoteUrlAllowed } from "./urlSafety-CiSuCJvX.js";
5
5
  import { t as ATTACHMENT_TEXT_LIMIT } from "./kind-Dc8x0HWz.js";
6
- import { _ as FsRunSnapshotStore, a as networkPolicyLibSource, c as bridgeError, ct as ProgressiveRouter, d as FsMemoryStore, l as parseBridgeRequest, m as AgentLoop, r as normalizeToolError, s as UPLOAD_FILES_UNAVAILABLE, t as CapabilityApproval, u as FsArtifactStore } from "./approval-BSwffz8P.js";
6
+ import { _ as FsRunSnapshotStore, a as networkPolicyLibSource, c as bridgeError, ct as ProgressiveRouter, d as FsMemoryStore, l as parseBridgeRequest, m as AgentLoop, r as normalizeToolError, s as UPLOAD_FILES_UNAVAILABLE, t as CapabilityApproval, u as FsArtifactStore } from "./approval-5BmspsRi.js";
7
7
  import { a as GoogleGenAiClient, o as AnthropicClient, s as OpenAiCompatibleClient, t as MockLlmClient } from "./llm-eIQNO9tr.js";
8
8
  import { c as toBase64$1, f as SPREADSHEET_MIME_TYPE } from "./linkedDocument-0iiVPd5t.js";
9
9
  import { n as normalizeToolContent, t as mergeCatalogEntries } from "./external-_ZRQe-V9.js";
10
- import { t as createWebSkillApi } from "./webSkillApi-C_7CTwtB.js";
10
+ import { t as createWebSkillApi } from "./webSkillApi-rX2e3SVB.js";
11
11
  import { A as frameSteps, D as SCROLL_DIRECTIONS, E as toActionFrameScopes, O as toFrameScopes, _ as defersImage, a as columnWidthToPixels, b as isPdfTextTrustworthy, c as isRangeTopLeft, d as rangeContains, f as readImageSize, k as frameLabel, l as parseCellAddress, n as EMU_PER_POINT, o as fitWithin, p as PDF_LARGE_IMAGE_RATIO, r as EMU_PER_TWIP$1, s as formatCellAddress, t as EMU_PER_PIXEL, u as parseCellRange, v as takesImage } from "./geometry-BXMPNuM6.js";
12
- import { zipSync } from "fflate";
12
+ import { Unzip, UnzipInflate, zipSync } from "fflate";
13
+ import { SaxesParser } from "saxes";
13
14
 
14
15
  //#region ../browser/src/fs/featureDetection.ts
15
16
  /** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
@@ -98,6 +99,21 @@ var OpfsProvider = class {
98
99
  return new Uint8Array(await (await handle.getFile()).arrayBuffer());
99
100
  });
100
101
  }
102
+ async createReadStream(p) {
103
+ const stream = (await this.#wrap(p, async () => (await this.#walkFile(p)).getFile())).stream();
104
+ return (async function* () {
105
+ const reader = stream.getReader();
106
+ try {
107
+ for (;;) {
108
+ const { done, value } = await reader.read();
109
+ if (done) return;
110
+ if (value !== void 0) yield value;
111
+ }
112
+ } finally {
113
+ reader.releaseLock();
114
+ }
115
+ })();
116
+ }
101
117
  async writeBinary(p, content) {
102
118
  await this.#wrap(p, async () => {
103
119
  const segments = this.#segments(p);
@@ -6437,12 +6453,12 @@ const XLSX_UNEXTRACTED = [
6437
6453
  "hidden row and column marking",
6438
6454
  "formula expressions (only cached values are read)"
6439
6455
  ];
6440
- const WORKBOOK = "xl/workbook.xml";
6441
- const WORKBOOK_RELS = "xl/_rels/workbook.xml.rels";
6442
- const SHARED_STRINGS = "xl/sharedStrings.xml";
6443
- const STYLES = "xl/styles.xml";
6456
+ const WORKBOOK$1 = "xl/workbook.xml";
6457
+ const WORKBOOK_RELS$1 = "xl/_rels/workbook.xml.rels";
6458
+ const SHARED_STRINGS$1 = "xl/sharedStrings.xml";
6459
+ const STYLES$1 = "xl/styles.xml";
6444
6460
  /** OLE2 复合文档签名:旧版 .xls 的开头,它不是 zip,走不到解压那一步 */
6445
- const OLE2_SIGNATURE = [
6461
+ const OLE2_SIGNATURE$1 = [
6446
6462
  208,
6447
6463
  207,
6448
6464
  17,
@@ -6467,14 +6483,14 @@ const BUILTIN_DATE_FORMATS = /* @__PURE__ */ new Set([
6467
6483
  46,
6468
6484
  47
6469
6485
  ]);
6470
- function fail(message) {
6486
+ function fail$1(message) {
6471
6487
  throw new WebSkillError("TOOL_EXECUTION_FAILED", message);
6472
6488
  }
6473
6489
  /** 按 localName 取子孙元素,忽略命名空间前缀:不同生成器的前缀写法并不统一 */
6474
- const byTag$3 = (scope, name) => [...scope.getElementsByTagNameNS("*", name)];
6475
- function parseXml$3(bytes, what) {
6490
+ const byTag$4 = (scope, name) => [...scope.getElementsByTagNameNS("*", name)];
6491
+ function parseXml$4(bytes, what) {
6476
6492
  const doc = new DOMParser().parseFromString(new TextDecoder().decode(bytes), "application/xml");
6477
- if (doc.getElementsByTagName("parsererror").length > 0) fail(`The workbook ${what} could not be parsed as XML.`);
6493
+ if (doc.getElementsByTagName("parsererror").length > 0) fail$1(`The workbook ${what} could not be parsed as XML.`);
6478
6494
  return doc;
6479
6495
  }
6480
6496
  /** `B` → 1、`AA` → 26;拿不到列号时返回 undefined,由调用方顺位补上 */
@@ -6519,14 +6535,14 @@ function isDateFormatCode(code) {
6519
6535
  /** 单元格样式索引 → 是否日期格式 */
6520
6536
  function readDateStyles(bytes) {
6521
6537
  if (bytes === void 0) return [];
6522
- const doc = parseXml$3(bytes, "styles");
6538
+ const doc = parseXml$4(bytes, "styles");
6523
6539
  const custom = /* @__PURE__ */ new Map();
6524
- for (const fmt of byTag$3(doc, "numFmt")) {
6540
+ for (const fmt of byTag$4(doc, "numFmt")) {
6525
6541
  const id = Number(fmt.getAttribute("numFmtId"));
6526
6542
  const code = fmt.getAttribute("formatCode");
6527
6543
  if (Number.isFinite(id) && code !== null) custom.set(id, code);
6528
6544
  }
6529
- const cellXfs = byTag$3(doc, "cellXfs")[0];
6545
+ const cellXfs = byTag$4(doc, "cellXfs")[0];
6530
6546
  if (cellXfs === void 0) return [];
6531
6547
  return [...cellXfs.children].filter((xf) => xf.localName === "xf").map((xf) => {
6532
6548
  const id = Number(xf.getAttribute("numFmtId") ?? "0");
@@ -6591,7 +6607,7 @@ function cellText$1(cell, shared, dateStyles, date1904) {
6591
6607
  function sheetRows(doc, shared, dateStyles, date1904) {
6592
6608
  const rows = [];
6593
6609
  let previousRow = 0;
6594
- for (const row of byTag$3(doc, "row")) {
6610
+ for (const row of byTag$4(doc, "row")) {
6595
6611
  const declared = Number(row.getAttribute("r"));
6596
6612
  const rowNumber = Number.isInteger(declared) && declared > 0 ? declared : previousRow + 1;
6597
6613
  for (let gap = previousRow + 1; gap < rowNumber; gap++) rows.push([]);
@@ -6614,9 +6630,9 @@ function sheetRows(doc, shared, dateStyles, date1904) {
6614
6630
  }
6615
6631
  /** 一行的文本:单元格用制表符分隔(与 docx 的表格同口径),行尾制表符不留 */
6616
6632
  const rowLine = (cells) => cells.join(" ").replace(/\t+$/, "");
6617
- const looksLikeOle2 = (bytes) => bytes.length >= OLE2_SIGNATURE.length && OLE2_SIGNATURE.every((byte, i) => bytes[i] === byte);
6633
+ const looksLikeOle2 = (bytes) => bytes.length >= OLE2_SIGNATURE$1.length && OLE2_SIGNATURE$1.every((byte, i) => bytes[i] === byte);
6618
6634
  /** rels 里的 Target 可能是相对 `xl/` 的,也可能是包根绝对路径 */
6619
- function resolveSheetPath(target) {
6635
+ function resolveSheetPath$1(target) {
6620
6636
  const clean = target.replace(/^\.\//, "");
6621
6637
  return clean.startsWith("/") ? clean.slice(1) : `xl/${clean}`;
6622
6638
  }
@@ -6626,37 +6642,37 @@ function resolveSheetPath(target) {
6626
6642
  * @throws WebSkillError 归档损坏、旧版 `.xls`、缺 `xl/workbook.xml`、工作表关系解不开、XML 解析失败
6627
6643
  */
6628
6644
  async function readXlsxWorkbook(bytes) {
6629
- if (looksLikeOle2(bytes)) fail("This is a legacy .xls workbook, which is not supported. Save it as .xlsx and try again.");
6645
+ if (looksLikeOle2(bytes)) fail$1("This is a legacy .xls workbook, which is not supported. Save it as .xlsx and try again.");
6630
6646
  let entries;
6631
6647
  try {
6632
6648
  entries = await unzipWithLimits(bytes);
6633
6649
  } catch {
6634
- fail("This file could not be read as an Excel workbook: the archive is damaged or not a .xlsx file.");
6650
+ fail$1("This file could not be read as an Excel workbook: the archive is damaged or not a .xlsx file.");
6635
6651
  }
6636
6652
  const parts = new Map(entries);
6637
- const workbookBytes = parts.get(WORKBOOK);
6638
- if (workbookBytes === void 0) fail(`This file is not an Excel workbook: it has no ${WORKBOOK} entry.`);
6639
- const workbook = parseXml$3(workbookBytes, "index");
6640
- const workbookPr = byTag$3(workbook, "workbookPr")[0];
6653
+ const workbookBytes = parts.get(WORKBOOK$1);
6654
+ if (workbookBytes === void 0) fail$1(`This file is not an Excel workbook: it has no ${WORKBOOK$1} entry.`);
6655
+ const workbook = parseXml$4(workbookBytes, "index");
6656
+ const workbookPr = byTag$4(workbook, "workbookPr")[0];
6641
6657
  const date1904 = workbookPr?.getAttribute("date1904") === "1" || workbookPr?.getAttribute("date1904") === "true";
6642
- const relsBytes = parts.get(WORKBOOK_RELS);
6658
+ const relsBytes = parts.get(WORKBOOK_RELS$1);
6643
6659
  const targets = /* @__PURE__ */ new Map();
6644
- if (relsBytes !== void 0) for (const rel of byTag$3(parseXml$3(relsBytes, "relationships"), "Relationship")) {
6660
+ if (relsBytes !== void 0) for (const rel of byTag$4(parseXml$4(relsBytes, "relationships"), "Relationship")) {
6645
6661
  const id = rel.getAttribute("Id");
6646
6662
  const target = rel.getAttribute("Target");
6647
- if (id !== null && target !== null) targets.set(id, resolveSheetPath(target));
6663
+ if (id !== null && target !== null) targets.set(id, resolveSheetPath$1(target));
6648
6664
  }
6649
- const sharedBytes = parts.get(SHARED_STRINGS);
6650
- const shared = sharedBytes === void 0 ? [] : byTag$3(parseXml$3(sharedBytes, "shared strings"), "si").map(sharedStringText);
6651
- const dateStyles = readDateStyles(parts.get(STYLES));
6665
+ const sharedBytes = parts.get(SHARED_STRINGS$1);
6666
+ const shared = sharedBytes === void 0 ? [] : byTag$4(parseXml$4(sharedBytes, "shared strings"), "si").map(sharedStringText);
6667
+ const dateStyles = readDateStyles(parts.get(STYLES$1));
6652
6668
  const sheets = [];
6653
- for (const [ordinal, sheet] of byTag$3(workbook, "sheet").entries()) {
6669
+ for (const [ordinal, sheet] of byTag$4(workbook, "sheet").entries()) {
6654
6670
  const name = sheet.getAttribute("name") ?? `Sheet${ordinal + 1}`;
6655
6671
  const relId = sheet.getAttributeNS("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id") ?? sheet.getAttribute("r:id");
6656
6672
  const path = relId !== null ? targets.get(relId) : void 0;
6657
6673
  const sheetBytes = path !== void 0 ? parts.get(path) : void 0;
6658
- if (path === void 0 || sheetBytes === void 0) fail(`The worksheet "${name}" could not be located inside the workbook.`);
6659
- const rows = sheetRows(parseXml$3(sheetBytes, `worksheet "${name}"`), shared, dateStyles, date1904);
6674
+ if (path === void 0 || sheetBytes === void 0) fail$1(`The worksheet "${name}" could not be located inside the workbook.`);
6675
+ const rows = sheetRows(parseXml$4(sheetBytes, `worksheet "${name}"`), shared, dateStyles, date1904);
6660
6676
  const lines = rows.map(rowLine);
6661
6677
  while (lines.length > 0 && lines[lines.length - 1] === "") {
6662
6678
  lines.pop();
@@ -6738,7 +6754,7 @@ const DEFAULT_BODY_TWIPS = 9360;
6738
6754
  function attr(node, name) {
6739
6755
  for (const item of node.attributes) if (item.name === name || item.localName === name) return item.value;
6740
6756
  }
6741
- function parseXml$2(bytes, what) {
6757
+ function parseXml$3(bytes, what) {
6742
6758
  const doc = new DOMParser().parseFromString(new TextDecoder().decode(bytes), "application/xml");
6743
6759
  if (doc.getElementsByTagName("parsererror").length > 0) throw new WebSkillError("TOOL_EXECUTION_FAILED", `The Word document's ${what} could not be parsed as XML.`);
6744
6760
  return doc;
@@ -6801,10 +6817,10 @@ async function readDocxBlocks(bytes) {
6801
6817
  const files = new Map(entries);
6802
6818
  const main = files.get(DOCUMENT$1);
6803
6819
  if (main === void 0) throw new WebSkillError("TOOL_EXECUTION_FAILED", `This file is not a Word document: it has no ${DOCUMENT$1} entry.`);
6804
- const doc = parseXml$2(main, "body");
6820
+ const doc = parseXml$3(main, "body");
6805
6821
  const targets = /* @__PURE__ */ new Map();
6806
6822
  const rels = files.get(RELS);
6807
- if (rels !== void 0) for (const rel of parseXml$2(rels, "relationships").getElementsByTagName("Relationship")) {
6823
+ if (rels !== void 0) for (const rel of parseXml$3(rels, "relationships").getElementsByTagName("Relationship")) {
6808
6824
  const id = attr(rel, "Id");
6809
6825
  const target = attr(rel, "Target");
6810
6826
  if (id !== void 0 && target !== void 0) targets.set(id, `word/${target.replace(/^\.?\//, "")}`);
@@ -6853,8 +6869,8 @@ const ANCHOR_TAGS = /* @__PURE__ */ new Set([
6853
6869
  /** 绝对锚点没有行号;放在表尾,保证图片不丢。 */
6854
6870
  const AFTER_LAST_ROW = Number.MAX_SAFE_INTEGER;
6855
6871
  const RELATIONSHIPS_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
6856
- const byTag$2 = (scope, name) => [...scope.getElementsByTagNameNS("*", name)];
6857
- function parseXml$1(bytes) {
6872
+ const byTag$3 = (scope, name) => [...scope.getElementsByTagNameNS("*", name)];
6873
+ function parseXml$2(bytes) {
6858
6874
  const doc = new DOMParser().parseFromString(new TextDecoder().decode(bytes), "application/xml");
6859
6875
  return doc.getElementsByTagName("parsererror").length > 0 ? void 0 : doc;
6860
6876
  }
@@ -6875,9 +6891,9 @@ const relsOf = (path) => `${dirOf(path)}_rels/${path.slice(path.lastIndexOf("/")
6875
6891
  function readRels(parts, path) {
6876
6892
  const out = /* @__PURE__ */ new Map();
6877
6893
  const bytes = parts.get(relsOf(path));
6878
- const doc = bytes === void 0 ? void 0 : parseXml$1(bytes);
6894
+ const doc = bytes === void 0 ? void 0 : parseXml$2(bytes);
6879
6895
  if (doc === void 0) return out;
6880
- for (const rel of byTag$2(doc, "Relationship")) {
6896
+ for (const rel of byTag$3(doc, "Relationship")) {
6881
6897
  const id = rel.getAttribute("Id");
6882
6898
  const target = rel.getAttribute("Target");
6883
6899
  if (id !== null && target !== null) out.set(id, resolveRelative$1(dirOf(path), target));
@@ -6885,7 +6901,7 @@ function readRels(parts, path) {
6885
6901
  return out;
6886
6902
  }
6887
6903
  const anchorNumber = (anchor, name) => {
6888
- const node = anchor === void 0 ? void 0 : byTag$2(anchor, name)[0];
6904
+ const node = anchor === void 0 ? void 0 : byTag$3(anchor, name)[0];
6889
6905
  const value = Number(node?.textContent ?? NaN);
6890
6906
  return Number.isInteger(value) && value >= 0 ? value : 0;
6891
6907
  };
@@ -6902,14 +6918,14 @@ function imagesOf(parts, sheetPath) {
6902
6918
  for (const [, target] of sheetRels) {
6903
6919
  if (!target.includes("/drawings/")) continue;
6904
6920
  const drawingBytes = parts.get(target);
6905
- const drawing = drawingBytes === void 0 ? void 0 : parseXml$1(drawingBytes);
6921
+ const drawing = drawingBytes === void 0 ? void 0 : parseXml$2(drawingBytes);
6906
6922
  if (drawing === void 0) continue;
6907
6923
  const drawingRels = readRels(parts, target);
6908
6924
  const anchors = [...drawing.documentElement?.children ?? []].filter((element) => ANCHOR_TAGS.has(element.localName));
6909
6925
  for (const anchor of anchors) {
6910
- const from = byTag$2(anchor, "from")[0];
6926
+ const from = byTag$3(anchor, "from")[0];
6911
6927
  const row = anchor.localName === "absoluteAnchor" || from === void 0 ? AFTER_LAST_ROW : anchorNumber(from, "row");
6912
- const blip = byTag$2(anchor, "blip")[0];
6928
+ const blip = byTag$3(anchor, "blip")[0];
6913
6929
  const relId = blip?.getAttributeNS(RELATIONSHIPS_NS, "embed") ?? blip?.getAttribute("r:embed");
6914
6930
  const path = relId === null || relId === void 0 ? void 0 : drawingRels.get(relId);
6915
6931
  const data = path === void 0 ? void 0 : parts.get(path);
@@ -6951,18 +6967,368 @@ async function readXlsxBlocks(bytes) {
6951
6967
  return { sheets: out };
6952
6968
  }
6953
6969
 
6970
+ //#endregion
6971
+ //#region ../browser/src/document/xlsxStreaming.ts
6972
+ const WORKBOOK = "xl/workbook.xml";
6973
+ const WORKBOOK_RELS = "xl/_rels/workbook.xml.rels";
6974
+ const SHARED_STRINGS = "xl/sharedStrings.xml";
6975
+ const STYLES = "xl/styles.xml";
6976
+ const OLE2_SIGNATURE = [
6977
+ 208,
6978
+ 207,
6979
+ 17,
6980
+ 224,
6981
+ 161,
6982
+ 177,
6983
+ 26,
6984
+ 225
6985
+ ];
6986
+ function fail(message) {
6987
+ throw new WebSkillError("TOOL_EXECUTION_FAILED", message);
6988
+ }
6989
+ async function sourceChunks(source) {
6990
+ if (typeof source !== "function") return (async function* () {
6991
+ yield source;
6992
+ })();
6993
+ return await source();
6994
+ }
6995
+ async function* entryChunks(source, path, state) {
6996
+ const output = [];
6997
+ let failure;
6998
+ const unzip = new Unzip();
6999
+ unzip.register(UnzipInflate);
7000
+ unzip.onfile = (file) => {
7001
+ if (file.name !== path) return;
7002
+ state.found = true;
7003
+ file.ondata = (error, chunk) => {
7004
+ if (error !== null) failure = error;
7005
+ else if (failure === void 0) output.push(chunk);
7006
+ };
7007
+ file.start();
7008
+ };
7009
+ try {
7010
+ const chunks = await sourceChunks(source);
7011
+ for await (const chunk of chunks) {
7012
+ unzip.push(chunk, false);
7013
+ yield* output.splice(0);
7014
+ await Promise.resolve();
7015
+ yield* output.splice(0);
7016
+ }
7017
+ unzip.push(/* @__PURE__ */ new Uint8Array(), true);
7018
+ await Promise.resolve();
7019
+ yield* output.splice(0);
7020
+ } catch (e) {
7021
+ failure ??= e;
7022
+ }
7023
+ if (failure !== void 0) {
7024
+ if (failure instanceof WebSkillError) throw failure;
7025
+ throw new WebSkillError("TOOL_EXECUTION_FAILED", `The workbook entry "${path}" could not be read: ${failure instanceof Error ? failure.message : String(failure)}`, failure);
7026
+ }
7027
+ }
7028
+ async function entryBytes(source, path) {
7029
+ const chunks = [];
7030
+ let total = 0;
7031
+ const state = { found: false };
7032
+ for await (const chunk of entryChunks(source, path, state)) {
7033
+ chunks.push(chunk);
7034
+ total += chunk.byteLength;
7035
+ }
7036
+ if (!state.found) return void 0;
7037
+ const out = new Uint8Array(total);
7038
+ let offset = 0;
7039
+ for (const chunk of chunks) {
7040
+ out.set(chunk, offset);
7041
+ offset += chunk.byteLength;
7042
+ }
7043
+ return out;
7044
+ }
7045
+ function parseXml$1(bytes, what) {
7046
+ const doc = new DOMParser().parseFromString(new TextDecoder().decode(bytes), "application/xml");
7047
+ if (doc.getElementsByTagName("parsererror").length > 0) fail(`The workbook ${what} could not be parsed as XML.`);
7048
+ return doc;
7049
+ }
7050
+ const byTag$2 = (scope, name) => [...scope.getElementsByTagNameNS("*", name)];
7051
+ const localName = (value) => {
7052
+ const name = typeof value === "string" ? value : value.name;
7053
+ return name.slice(name.lastIndexOf(":") + 1);
7054
+ };
7055
+ function attribute(tag, name) {
7056
+ const direct = tag.attributes[name];
7057
+ if (direct !== void 0) return direct;
7058
+ const key = Object.keys(tag.attributes).find((candidate) => candidate.endsWith(`:${name}`));
7059
+ return key === void 0 ? null : tag.attributes[key] ?? null;
7060
+ }
7061
+ function newParser(onError) {
7062
+ const parser = new SaxesParser();
7063
+ parser.on("error", onError);
7064
+ return parser;
7065
+ }
7066
+ async function readSharedStrings(source) {
7067
+ const shared = [];
7068
+ let inside = false;
7069
+ let textDepth = 0;
7070
+ let phoneticDepth = 0;
7071
+ let text = "";
7072
+ let parseError;
7073
+ const parser = newParser((error) => {
7074
+ parseError = error;
7075
+ });
7076
+ parser.on("opentag", (tag) => {
7077
+ const name = localName(tag.name);
7078
+ if (name === "si") {
7079
+ inside = true;
7080
+ text = "";
7081
+ } else if (inside && name === "rPh") phoneticDepth += 1;
7082
+ else if (inside && name === "t" && phoneticDepth === 0) textDepth += 1;
7083
+ });
7084
+ parser.on("text", (value) => {
7085
+ if (inside && textDepth > 0 && phoneticDepth === 0) text += value;
7086
+ });
7087
+ parser.on("cdata", (value) => {
7088
+ if (inside && textDepth > 0 && phoneticDepth === 0) text += value;
7089
+ });
7090
+ parser.on("closetag", (nameValue) => {
7091
+ const name = localName(nameValue);
7092
+ if (name === "t" && textDepth > 0) textDepth -= 1;
7093
+ else if (name === "rPh" && phoneticDepth > 0) phoneticDepth -= 1;
7094
+ else if (name === "si" && inside) {
7095
+ shared.push(text);
7096
+ inside = false;
7097
+ text = "";
7098
+ }
7099
+ });
7100
+ const state = { found: false };
7101
+ const decoder = new TextDecoder();
7102
+ for await (const chunk of entryChunks(source, SHARED_STRINGS, state)) parser.write(decoder.decode(chunk, { stream: true }));
7103
+ if (!state.found) return [];
7104
+ parser.write(decoder.decode());
7105
+ parser.close();
7106
+ if (parseError !== void 0) fail(`The workbook shared strings could not be parsed as XML: ${parseError.message}`);
7107
+ return shared;
7108
+ }
7109
+ function scanWorksheet(source, path, shared, dateStyles, date1904) {
7110
+ return (async function* () {
7111
+ const queue = [];
7112
+ let previousRow = 0;
7113
+ let lastYielded = 0;
7114
+ let rowNumber = 0;
7115
+ let cells = [];
7116
+ let nextColumn = 0;
7117
+ let cell;
7118
+ let parseError;
7119
+ const parser = newParser((error) => {
7120
+ parseError = error;
7121
+ });
7122
+ const drain = async function* () {
7123
+ for (const entry of queue.splice(0)) {
7124
+ for (let gap = lastYielded + 1; gap < entry.row; gap += 1) yield {
7125
+ row: gap,
7126
+ cells: []
7127
+ };
7128
+ lastYielded = entry.row;
7129
+ yield entry;
7130
+ }
7131
+ };
7132
+ parser.on("opentag", (tag) => {
7133
+ const name = localName(tag.name);
7134
+ if (name === "row") {
7135
+ const declared = Number(attribute(tag, "r"));
7136
+ rowNumber = Number.isInteger(declared) && declared > 0 ? declared : previousRow + 1;
7137
+ previousRow = rowNumber;
7138
+ cells = [];
7139
+ nextColumn = 0;
7140
+ return;
7141
+ }
7142
+ if (name === "c") {
7143
+ const column = columnIndex(attribute(tag, "r")) ?? nextColumn;
7144
+ cell = {
7145
+ type: attribute(tag, "t"),
7146
+ style: Number(attribute(tag, "s") ?? "0"),
7147
+ column,
7148
+ raw: "",
7149
+ inline: "",
7150
+ valueDepth: 0,
7151
+ inlineDepth: 0,
7152
+ textDepth: 0,
7153
+ phoneticDepth: 0
7154
+ };
7155
+ nextColumn = column + 1;
7156
+ } else if (cell !== void 0 && name === "v") cell.valueDepth += 1;
7157
+ else if (cell !== void 0 && name === "is") cell.inlineDepth += 1;
7158
+ else if (cell !== void 0 && name === "rPh" && cell.inlineDepth > 0) cell.phoneticDepth += 1;
7159
+ else if (cell !== void 0 && name === "t" && cell.inlineDepth > 0 && cell.phoneticDepth === 0) cell.textDepth += 1;
7160
+ });
7161
+ parser.on("text", (value) => {
7162
+ if (cell === void 0) return;
7163
+ if (cell.valueDepth > 0) cell.raw += value;
7164
+ if (cell.textDepth > 0 && cell.phoneticDepth === 0) cell.inline += value;
7165
+ });
7166
+ parser.on("cdata", (value) => {
7167
+ if (cell === void 0) return;
7168
+ if (cell.valueDepth > 0) cell.raw += value;
7169
+ if (cell.textDepth > 0 && cell.phoneticDepth === 0) cell.inline += value;
7170
+ });
7171
+ parser.on("closetag", (nameValue) => {
7172
+ const name = localName(nameValue);
7173
+ if (cell !== void 0 && name === "t" && cell.textDepth > 0) cell.textDepth -= 1;
7174
+ else if (cell !== void 0 && name === "rPh" && cell.phoneticDepth > 0) cell.phoneticDepth -= 1;
7175
+ else if (cell !== void 0 && name === "is" && cell.inlineDepth > 0) cell.inlineDepth -= 1;
7176
+ else if (cell !== void 0 && name === "v" && cell.valueDepth > 0) cell.valueDepth -= 1;
7177
+ else if (name === "c" && cell !== void 0) {
7178
+ let text = cell.raw;
7179
+ if (cell.type === "inlineStr") text = cell.inline;
7180
+ else if (cell.type === "s") {
7181
+ const index = Number(cell.raw);
7182
+ text = Number.isInteger(index) ? shared[index] ?? "" : "";
7183
+ } else if (cell.type === "b") text = cell.raw === "1" ? "TRUE" : "FALSE";
7184
+ else if (cell.type !== "str" && cell.type !== "e" && cell.raw !== "") {
7185
+ const numeric = Number(cell.raw);
7186
+ if (Number.isFinite(numeric) && Number.isInteger(cell.style) && dateStyles[cell.style] === true) text = serialToIso(numeric, date1904);
7187
+ }
7188
+ for (let gap = cells.length; gap < cell.column; gap += 1) cells.push("");
7189
+ if (cells.length > cell.column) cells[cell.column] = text;
7190
+ else cells.push(text);
7191
+ cell = void 0;
7192
+ } else if (name === "row") {
7193
+ while (cells.length > 0 && cells[cells.length - 1] === "") cells.pop();
7194
+ if (cells.length > 0) queue.push({
7195
+ row: rowNumber,
7196
+ cells
7197
+ });
7198
+ cells = [];
7199
+ }
7200
+ });
7201
+ const state = { found: false };
7202
+ const decoder = new TextDecoder();
7203
+ for await (const chunk of entryChunks(source, path, state)) {
7204
+ parser.write(decoder.decode(chunk, { stream: true }));
7205
+ yield* drain();
7206
+ }
7207
+ if (!state.found) fail(`The worksheet entry "${path}" could not be located inside the workbook.`);
7208
+ parser.write(decoder.decode());
7209
+ parser.close();
7210
+ if (parseError !== void 0) fail(`The worksheet "${path}" could not be parsed as XML: ${parseError.message}`);
7211
+ yield* drain();
7212
+ })();
7213
+ }
7214
+ function blank(cells) {
7215
+ return cells.every((value) => value.trim() === "");
7216
+ }
7217
+ function wide(code) {
7218
+ return code >= 19968 && code <= 40959 || code >= 13312 && code <= 19903 || code >= 12288 && code <= 12543 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65280 && code <= 65519;
7219
+ }
7220
+ const MAX_CACHED_ROWS = 5e4;
7221
+ const MAX_CACHED_CELLS = 5e5;
7222
+ async function measure(sheet) {
7223
+ let rowCount = 0;
7224
+ let columnCount = 0;
7225
+ let firstNonEmptyRow = 0;
7226
+ let firstRow = [];
7227
+ let dataRows = 0;
7228
+ let wideChars = 0;
7229
+ let otherChars = sheet.name.length + 8;
7230
+ let cellCount = 0;
7231
+ const rows = [];
7232
+ for await (const entry of sheet.scan()) {
7233
+ rowCount += 1;
7234
+ cellCount += entry.cells.length;
7235
+ if (rowCount <= MAX_CACHED_ROWS && cellCount <= MAX_CACHED_CELLS) rows.push(entry);
7236
+ columnCount = Math.max(columnCount, entry.cells.length);
7237
+ otherChars += Math.max(entry.cells.length - 1, 0) + 1;
7238
+ for (const value of entry.cells) for (let index = 0; index < value.length; index += 1) if (wide(value.charCodeAt(index))) wideChars += 1;
7239
+ else otherChars += 1;
7240
+ if (blank(entry.cells)) continue;
7241
+ if (firstNonEmptyRow === 0) {
7242
+ firstNonEmptyRow = entry.row;
7243
+ firstRow = entry.cells;
7244
+ } else dataRows += 1;
7245
+ }
7246
+ return {
7247
+ meta: {
7248
+ rowCount,
7249
+ columnCount,
7250
+ firstNonEmptyRow,
7251
+ firstRow,
7252
+ dataRows,
7253
+ estimatedTokens: wideChars + Math.ceil(otherChars / 4),
7254
+ textChars: wideChars + otherChars
7255
+ },
7256
+ ...rowCount <= MAX_CACHED_ROWS && cellCount <= MAX_CACHED_CELLS ? { rows } : {}
7257
+ };
7258
+ }
7259
+ function resolveSheetPath(target) {
7260
+ const clean = target.replace(/^\.\//, "");
7261
+ return clean.startsWith("/") ? clean.slice(1) : `xl/${clean}`;
7262
+ }
7263
+ async function readStreamingXlsxWorkbook(source) {
7264
+ if (typeof source !== "function" && OLE2_SIGNATURE.every((byte, index) => source[index] === byte)) fail("This is a legacy .xls workbook, which is not supported. Save it as .xlsx and try again.");
7265
+ const workbookBytes = await entryBytes(source, WORKBOOK);
7266
+ if (workbookBytes === void 0) fail(`This file is not an Excel workbook: it has no ${WORKBOOK} entry.`);
7267
+ const workbook = parseXml$1(workbookBytes, "index");
7268
+ const workbookPr = byTag$2(workbook, "workbookPr")[0];
7269
+ const date1904 = workbookPr?.getAttribute("date1904") === "1" || workbookPr?.getAttribute("date1904") === "true";
7270
+ const relsBytes = await entryBytes(source, WORKBOOK_RELS);
7271
+ const targets = /* @__PURE__ */ new Map();
7272
+ if (relsBytes !== void 0) for (const rel of byTag$2(parseXml$1(relsBytes, "relationships"), "Relationship")) {
7273
+ const id = rel.getAttribute("Id");
7274
+ const target = rel.getAttribute("Target");
7275
+ if (id !== null && target !== null) targets.set(id, resolveSheetPath(target));
7276
+ }
7277
+ const [shared, dateStyles] = await Promise.all([readSharedStrings(source), entryBytes(source, STYLES).then((bytes) => readDateStyles(bytes))]);
7278
+ const sheets = [];
7279
+ for (const [ordinal, sheet] of byTag$2(workbook, "sheet").entries()) {
7280
+ const name = sheet.getAttribute("name") ?? `Sheet${ordinal + 1}`;
7281
+ const relId = sheet.getAttributeNS("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id") ?? sheet.getAttribute("r:id");
7282
+ const path = relId !== null ? targets.get(relId) : void 0;
7283
+ if (path === void 0) fail(`The worksheet "${name}" could not be located inside the workbook.`);
7284
+ let metadata;
7285
+ let cachedRows;
7286
+ const scan = () => {
7287
+ const rows = cachedRows;
7288
+ if (rows !== void 0) return (async function* () {
7289
+ yield* rows;
7290
+ })();
7291
+ return scanWorksheet(source, path, shared, dateStyles, date1904);
7292
+ };
7293
+ const entry = {
7294
+ name,
7295
+ scan,
7296
+ meta: () => {
7297
+ metadata ??= measure({
7298
+ name,
7299
+ scan
7300
+ }).then((result) => {
7301
+ cachedRows = result.rows;
7302
+ return result.meta;
7303
+ }).catch((error) => {
7304
+ metadata = void 0;
7305
+ throw error;
7306
+ });
7307
+ return metadata;
7308
+ }
7309
+ };
7310
+ sheets.push(entry);
7311
+ }
7312
+ return {
7313
+ kind: "streaming",
7314
+ sheets
7315
+ };
7316
+ }
7317
+
6954
7318
  //#endregion
6955
7319
  //#region ../browser/src/document/xlsxTable.ts
6956
7320
  /**
6957
- * xlsx → 单元格网格(0.26.0 分册 10 · FR-10.20)。
7321
+ * xlsx → 单元格网格或可重复扫描的行流。
6958
7322
  *
6959
- * 与 `extractXlsxText` 共用 `readXlsxWorkbook` 的同一份解析(单元格取值、日期换算、空格补位),
6960
- * 所以画像与查询看到的格,和模型从文本里读到的格是同一批。
6961
- * 错误语义同 `extractXlsxText`:旧版 `.xls`、归档损坏、缺 `xl/workbook.xml` 都抛 `WebSkillError`。
7323
+ * `read` 保留既有网格契约;`readStreaming` 用 SAX 逐行解析工作表,不受整包解压的 64 MiB 条目上限约束。
7324
+ * 两条路径使用同一套单元格取值、日期换算与空格补位规则,错误均带 `WebSkillError`。
6962
7325
  * @experimental
6963
7326
  */
6964
7327
  function createXlsxTableReader() {
6965
- return { read: readXlsxTable };
7328
+ return {
7329
+ read: readXlsxTable,
7330
+ readStreaming: readStreamingXlsxWorkbook
7331
+ };
6966
7332
  }
6967
7333
  async function readXlsxTable(bytes) {
6968
7334
  const { sheets } = await readXlsxWorkbook(bytes);