@webskill/sdk 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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 { c as formatAttachmentText, o as XLSX_MIME, s as classifyAttachment } from "./kind-DaKqLX2F.js";
6
+ import { c as SPREADSHEET_EXTENSION, f as DEFAULT_MAX_DATA_SOURCE_BYTES, l as SPREADSHEET_LIMITS, o as toBase64, s as encodeSpreadsheet, u as SPREADSHEET_MIME_TYPE } from "./linkedDocument-C0gj1sMq.js";
7
+ import { _ as frameSteps, a as defersImage, c as isPdfTextTrustworthy, d as DOCUMENT_READ_BUDGET, f as PAGE_ACTION_KINDS, g as frameLabel, h as toFrameScopes, i as asReadMode, l as measurePdfTextQuality, m as toActionFrameScopes, n as READ_DOCUMENT_TOOL, o as takesImage, p as PAGE_DOCUMENT_REF, r as createDocumentToolSource, s as PDF_GARBLED_RATIO, t as PDF_LARGE_IMAGE_RATIO, u as sniffDocumentFormat } from "./toolSource-C9oNuKmr.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$5 = {
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$7(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$7("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$7("VALIDATION_FAILED", "The \"id\" argument is required for action \"update\""));
243
+ if (status !== void 0 && typeof status !== "string") return Promise.resolve(toolError$7("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$7("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$7(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$5
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$4 = {
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$6(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$6("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$6(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$4
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$3 = {
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$5(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$5("VALIDATION_FAILED", "The \"todoId\" argument is required and must be a string");
829
+ if (typeof task !== "string" || task.trim() === "") return toolError$5("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$5("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$5(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$3
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$2 = "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$2 = {
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$4(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$2,
1405
+ inputSchema: INPUT_SCHEMA$2
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$4("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$4("PERCEPTION_CURSOR_INVALID", CURSOR_REJECTED);
1423
+ if (ref !== void 0 && ref !== pending.subtreeRef) return toolError$4("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$4(code, messageOf(e));
1486
+ return toolError$4("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$1(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$1(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;
@@ -2105,13 +2106,13 @@ const PAGE_ACTION_SYSTEM_PROMPT = [
2105
2106
  //#endregion
2106
2107
  //#region ../agent/src/pageAction/toolSource.ts
2107
2108
  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.";
2109
+ const DESCRIPTION$1 = "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
2110
  /**
2110
2111
  * 入参里**没有** `scope` / `selector` / `include` / `frame` 一类字段(AC-23.2):
2111
2112
  * 工具一旦接受它们,「能操作哪里」就有了一条从模型输出流进来的路径。
2112
2113
  * 模型能表达的只有「在哪个句柄上做哪个动作」。
2113
2114
  */
2114
- const INPUT_SCHEMA = {
2115
+ const INPUT_SCHEMA$1 = {
2115
2116
  type: "object",
2116
2117
  properties: {
2117
2118
  ref: {
@@ -2130,7 +2131,7 @@ const INPUT_SCHEMA = {
2130
2131
  required: ["action"],
2131
2132
  additionalProperties: false
2132
2133
  };
2133
- function toolError$2(code, message) {
2134
+ function toolError$3(code, message) {
2134
2135
  return {
2135
2136
  ok: false,
2136
2137
  content: [],
@@ -2153,8 +2154,8 @@ function createPageActionToolSource(options) {
2153
2154
  kind: "page-action",
2154
2155
  listToolSpecs: () => Promise.resolve(policy.enabled ? [{
2155
2156
  name: PAGE_ACTION_TOOL,
2156
- description: DESCRIPTION,
2157
- inputSchema: INPUT_SCHEMA
2157
+ description: DESCRIPTION$1,
2158
+ inputSchema: INPUT_SCHEMA$1
2158
2159
  }] : []),
2159
2160
  systemPrompt: () => Promise.resolve(policy.enabled ? PAGE_ACTION_SYSTEM_PROMPT : void 0),
2160
2161
  canHandle: (name) => name === PAGE_ACTION_TOOL,
@@ -2180,7 +2181,7 @@ function createPageActionToolSource(options) {
2180
2181
  },
2181
2182
  call: async (_name, args, context) => {
2182
2183
  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(" / ")}.`);
2184
+ if (typeof action !== "string" || !PAGE_ACTION_KINDS.includes(action)) return toolError$3("VALIDATION_FAILED", `act_on_page needs one of ${PAGE_ACTION_KINDS.join(" / ")}.`);
2184
2185
  const runContext = { ...context?.runId !== void 0 ? { runId: context.runId } : {} };
2185
2186
  const run = async (request) => {
2186
2187
  try {
@@ -2197,25 +2198,25 @@ function createPageActionToolSource(options) {
2197
2198
  } }
2198
2199
  };
2199
2200
  } catch (e) {
2200
- return toolError$2(e instanceof WebSkillError ? e.code : "PAGE_ACTION_FAILED", messageOf(e));
2201
+ return toolError$3(e instanceof WebSkillError ? e.code : "PAGE_ACTION_FAILED", messageOf(e));
2201
2202
  }
2202
2203
  };
2203
2204
  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\".");
2205
+ if ("ref" in args || "value" in args) return toolError$3("VALIDATION_FAILED", "act_on_page with \"back\" takes no \"ref\" and no \"value\".");
2205
2206
  return await run({
2206
2207
  ref: PAGE_DOCUMENT_REF,
2207
2208
  action: "back"
2208
2209
  });
2209
2210
  }
2210
2211
  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}".`);
2212
+ if (typeof ref !== "string") return toolError$3("VALIDATION_FAILED", `act_on_page needs a "ref" string when the action is "${action}".`);
2212
2213
  const value = args["value"];
2213
2214
  if ([
2214
2215
  "fill",
2215
2216
  "select",
2216
2217
  "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\".");
2218
+ ].includes(action) && typeof value !== "string") return toolError$3("VALIDATION_FAILED", `act_on_page needs a "value" string when the action is "${action}".`);
2219
+ if (action === "scroll" && value !== "down" && value !== "up") return toolError$3("VALIDATION_FAILED", "act_on_page with \"scroll\" needs a \"value\" of \"down\" or \"up\".");
2219
2220
  return await run({
2220
2221
  ref,
2221
2222
  action,
@@ -2457,7 +2458,7 @@ const SPECS = [
2457
2458
  inputSchema: TAB_SCHEMA
2458
2459
  }
2459
2460
  ];
2460
- function toolError$1(code, message) {
2461
+ function toolError$2(code, message) {
2461
2462
  return {
2462
2463
  ok: false,
2463
2464
  content: [],
@@ -2492,7 +2493,7 @@ function createTabWorksetToolSource(options) {
2492
2493
  }]
2493
2494
  };
2494
2495
  const handle = args["tab"];
2495
- if (typeof handle !== "string" || handle === "") return toolError$1("VALIDATION_FAILED", `${name} needs a "tab" handle from list_page_tabs.`);
2496
+ if (typeof handle !== "string" || handle === "") return toolError$2("VALIDATION_FAILED", `${name} needs a "tab" handle from list_page_tabs.`);
2496
2497
  return {
2497
2498
  ok: true,
2498
2499
  content: [{
@@ -2501,7 +2502,7 @@ function createTabWorksetToolSource(options) {
2501
2502
  }]
2502
2503
  };
2503
2504
  } catch (e) {
2504
- return toolError$1(e instanceof WebSkillError ? e.code : "PAGE_TAB_FAILED", messageOf(e));
2505
+ return toolError$2(e instanceof WebSkillError ? e.code : "PAGE_TAB_FAILED", messageOf(e));
2505
2506
  }
2506
2507
  }
2507
2508
  };
@@ -2622,6 +2623,42 @@ var DownloadedFilePolicy = class {
2622
2623
  });
2623
2624
  return result;
2624
2625
  }
2626
+ /**
2627
+ * 取原始字节,不做形态化(0.21.0 分册 22 · FR-22.4)。
2628
+ *
2629
+ * 授权卡、审计留痕与 `read()` 完全一致——逐单元读文档要的是字节本身,
2630
+ * 但绝不能因此绕过策略:那等于给「读用户本机磁盘」开第二个没有授权的入口。
2631
+ * @experimental
2632
+ */
2633
+ async readBytes(id) {
2634
+ const reader = this.#reader();
2635
+ const entry = await this.#resolve(id);
2636
+ const approved = await this.#authorize("read", entry);
2637
+ if (approved === false) {
2638
+ const reason = `Reading "${entry.name}" was declined by the user.`;
2639
+ await this.#record({
2640
+ action: "read",
2641
+ ...describe(entry),
2642
+ approved: false,
2643
+ ok: false,
2644
+ reason
2645
+ });
2646
+ throw new WebSkillError("TOOL_DENIED", reason);
2647
+ }
2648
+ const { contentType, bytes } = await reader.read(id);
2649
+ await this.#record({
2650
+ action: "read",
2651
+ ...describe(entry),
2652
+ contentType,
2653
+ approved,
2654
+ ok: true
2655
+ });
2656
+ return {
2657
+ entry,
2658
+ contentType,
2659
+ bytes
2660
+ };
2661
+ }
2625
2662
  async #materialize(entry, contentType, kind, bytes) {
2626
2663
  if (kind === "text" || kind === "document-text") {
2627
2664
  const body = kind === "text" ? new TextDecoder().decode(bytes) : await this.#extract(contentType, entry.name)(bytes);
@@ -2629,15 +2666,10 @@ var DownloadedFilePolicy = class {
2629
2666
  entry,
2630
2667
  contentType,
2631
2668
  kind,
2632
- text: formatAttachmentText({
2633
- name: entry.name,
2634
- contentType,
2635
- body,
2636
- kind,
2637
- origin: "download"
2638
- })
2669
+ text: this.#label(entry, contentType, body, kind)
2639
2670
  };
2640
2671
  }
2672
+ if (kind === "file" && this.#overDocumentLimit(bytes)) return await this.#pdfAsText(entry, contentType, bytes);
2641
2673
  return {
2642
2674
  entry,
2643
2675
  contentType,
@@ -2645,6 +2677,43 @@ var DownloadedFilePolicy = class {
2645
2677
  bytes
2646
2678
  };
2647
2679
  }
2680
+ /** 与手动上传共用同一个格式化器:两条入口的产出必须逐字一致(AC-20.6) */
2681
+ #label(entry, contentType, body, kind) {
2682
+ return formatAttachmentText({
2683
+ name: entry.name,
2684
+ contentType,
2685
+ body,
2686
+ kind,
2687
+ origin: "download"
2688
+ });
2689
+ }
2690
+ /** base64 编码后才是请求体里的长度,量原始字节会漏掉那三分之一(与上传侧同口径) */
2691
+ #overDocumentLimit(bytes) {
2692
+ const max = this.#options.maxDocumentBytes?.();
2693
+ return max !== void 0 && Math.ceil(bytes.length / 3) * 4 > max;
2694
+ }
2695
+ /**
2696
+ * 超阈值 PDF 退到抽取文本(AC-21.2),抽不出就**拒绝并说明**(AC-21.3):
2697
+ * 半份原件是坏文件,半份抽取文本会让模型以为自己读全了。与上传侧 `#routeDocument` 同一套判断。
2698
+ */
2699
+ async #pdfAsText(entry, contentType, bytes) {
2700
+ 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.`;
2701
+ const extract = this.#options.pdfExtractor;
2702
+ 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.`);
2703
+ let body;
2704
+ try {
2705
+ body = await extract(bytes);
2706
+ } catch (e) {
2707
+ 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.`);
2708
+ }
2709
+ 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.`);
2710
+ return {
2711
+ entry,
2712
+ contentType,
2713
+ kind: "document-text",
2714
+ text: this.#label(entry, contentType, body, "document-text")
2715
+ };
2716
+ }
2648
2717
  #extract(contentType, name) {
2649
2718
  const extract = isSpreadsheet(contentType, name) ? this.#options.xlsxExtractor : this.#options.docxExtractor;
2650
2719
  if (extract === void 0) throw new WebSkillError("TOOL_UNSUPPORTED", this.#noExtractorReason(contentType, name));
@@ -2756,8 +2825,8 @@ const READ_INPUT_SCHEMA = {
2756
2825
  required: ["id"],
2757
2826
  additionalProperties: false
2758
2827
  };
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) {
2828
+ const SYSTEM_PROMPT$1 = "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.";
2829
+ function toolError$1(e) {
2761
2830
  return {
2762
2831
  ok: false,
2763
2832
  content: [],
@@ -2787,7 +2856,7 @@ function createDownloadedFileToolSource(options) {
2787
2856
  description: READ_DESCRIPTION,
2788
2857
  inputSchema: READ_INPUT_SCHEMA
2789
2858
  }] : []),
2790
- systemPrompt: () => Promise.resolve(policy.enabled ? SYSTEM_PROMPT : void 0),
2859
+ systemPrompt: () => Promise.resolve(policy.enabled ? SYSTEM_PROMPT$1 : void 0),
2791
2860
  canHandle: (name) => name === "list_downloaded_files" || name === "read_downloaded_file",
2792
2861
  argCaptureTrust: (name) => name === "list_downloaded_files" || name === "read_downloaded_file" ? { tier: "reviewed" } : void 0,
2793
2862
  call: async (name, args) => {
@@ -2806,7 +2875,7 @@ function createDownloadedFileToolSource(options) {
2806
2875
  content: readContent(await policy.read(id))
2807
2876
  };
2808
2877
  } catch (e) {
2809
- return toolError(e);
2878
+ return toolError$1(e);
2810
2879
  }
2811
2880
  }
2812
2881
  };
@@ -2847,4 +2916,151 @@ function readContent(result) {
2847
2916
  }
2848
2917
 
2849
2918
  //#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 };
2919
+ //#region ../agent/src/spreadsheet/toolSource.ts
2920
+ const EXPORT_SPREADSHEET_TOOL = "export_spreadsheet";
2921
+ const DESCRIPTION = "Write tabular data to an Excel (.xlsx) file the user can download.";
2922
+ const INPUT_SCHEMA = {
2923
+ type: "object",
2924
+ properties: {
2925
+ fileName: {
2926
+ type: "string",
2927
+ description: "Name for the workbook, for example \"sales-2026.xlsx\". A missing .xlsx suffix is added for you."
2928
+ },
2929
+ spec: {
2930
+ type: "object",
2931
+ properties: { sheets: {
2932
+ type: "array",
2933
+ minItems: 1,
2934
+ maxItems: SPREADSHEET_LIMITS.sheets,
2935
+ items: {
2936
+ type: "object",
2937
+ properties: {
2938
+ name: {
2939
+ type: "string",
2940
+ description: "Sheet name: at most 31 characters, and none of : \\ / ? * [ ]"
2941
+ },
2942
+ columns: {
2943
+ type: "array",
2944
+ items: { type: "string" },
2945
+ description: "Optional header row. When given it becomes the first, bold row."
2946
+ },
2947
+ rows: {
2948
+ type: "array",
2949
+ maxItems: SPREADSHEET_LIMITS.rowsPerSheet,
2950
+ items: {
2951
+ type: "array",
2952
+ maxItems: SPREADSHEET_LIMITS.columnsPerSheet,
2953
+ items: {
2954
+ anyOf: [
2955
+ { type: "string" },
2956
+ { type: "number" },
2957
+ { type: "boolean" },
2958
+ { type: "null" }
2959
+ ],
2960
+ description: "Text is never re-parsed as a number or a date: \"0123\" stays \"0123\". Use null for an empty cell."
2961
+ }
2962
+ }
2963
+ }
2964
+ },
2965
+ required: ["name", "rows"],
2966
+ additionalProperties: false
2967
+ }
2968
+ } },
2969
+ required: ["sheets"],
2970
+ additionalProperties: false
2971
+ }
2972
+ },
2973
+ required: ["fileName", "spec"],
2974
+ additionalProperties: false
2975
+ };
2976
+ const SYSTEM_PROMPT = "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.";
2977
+ function toolError(e) {
2978
+ return {
2979
+ ok: false,
2980
+ content: [],
2981
+ error: {
2982
+ code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
2983
+ message: messageOf(e)
2984
+ }
2985
+ };
2986
+ }
2987
+ const isRecord = (v) => typeof v === "object" && v !== null;
2988
+ /**
2989
+ * FR-10.3a 的两条分支。忘写后缀是模型的高频行为,为它打断一整轮不划算;
2990
+ * 写成 `.csv` 则是**意图冲突**而不是疏漏,必须拒。
2991
+ *
2992
+ * 路径安全不在这里做:`writeArtifact` 那一条(`resolveInsideRoot`)是唯一的一份,
2993
+ * FR-10.3b 明确禁止另写一份。
2994
+ */
2995
+ function resolveFileName(value) {
2996
+ if (typeof value !== "string" || value.trim() === "") throw new WebSkillError("VALIDATION_FAILED", "export_spreadsheet requires a non-empty \"fileName\".");
2997
+ const fileName = value.trim();
2998
+ const lastSegment = fileName.split(/[\\/]/).pop() ?? "";
2999
+ const dot = lastSegment.lastIndexOf(".");
3000
+ const extension = dot <= 0 ? "" : lastSegment.slice(dot + 1);
3001
+ if (extension === "") return `${fileName}${SPREADSHEET_EXTENSION}`;
3002
+ if (extension.toLowerCase() === "xlsx") return fileName;
3003
+ throw new WebSkillError("VALIDATION_FAILED", `export_spreadsheet writes .xlsx files; "${fileName}" asks for .${extension}. Drop the suffix or use .xlsx.`);
3004
+ }
3005
+ function summarize(spec) {
3006
+ const sheets = spec.sheets;
3007
+ return {
3008
+ sheetCount: sheets.length,
3009
+ rowCount: sheets.reduce((total, sheet) => total + sheet.rows.length, 0)
3010
+ };
3011
+ }
3012
+ /**
3013
+ * 把表格导出接到既有工具协议上。
3014
+ *
3015
+ * 做成 `ExternalToolSource` 而不是 `AgentLoop` 的内置数组(DV-4):本工具需要
3016
+ * `argCaptureTrust` 与 `captureArgs` 两个钩子,内置数组里没有这两个概念。
3017
+ * @experimental
3018
+ */
3019
+ function createSpreadsheetToolSource(options) {
3020
+ const { writeArtifact } = options;
3021
+ return {
3022
+ kind: "spreadsheet",
3023
+ listToolSpecs: () => Promise.resolve(writeArtifact ? [{
3024
+ name: EXPORT_SPREADSHEET_TOOL,
3025
+ description: DESCRIPTION,
3026
+ inputSchema: INPUT_SCHEMA
3027
+ }] : []),
3028
+ systemPrompt: () => Promise.resolve(writeArtifact ? SYSTEM_PROMPT : void 0),
3029
+ canHandle: (name) => name === EXPORT_SPREADSHEET_TOOL,
3030
+ captureArgs: (_name, args) => {
3031
+ const spec = args["spec"];
3032
+ const sheets = isRecord(spec) && Array.isArray(spec["sheets"]) ? spec["sheets"] : [];
3033
+ const rowCount = sheets.reduce((total, sheet) => total + (isRecord(sheet) && Array.isArray(sheet["rows"]) ? sheet["rows"].length : 0), 0);
3034
+ return {
3035
+ ...typeof args["fileName"] === "string" ? { fileName: args["fileName"] } : {},
3036
+ sheetCount: sheets.length,
3037
+ rowCount
3038
+ };
3039
+ },
3040
+ call: async (name, args, context) => {
3041
+ try {
3042
+ if (name !== "export_spreadsheet" || !writeArtifact) throw new WebSkillError("TOOL_UNSUPPORTED", `${name} is not available in this host.`);
3043
+ const fileName = resolveFileName(args["fileName"]);
3044
+ const spec = args["spec"];
3045
+ const bytes = await encodeSpreadsheet(spec);
3046
+ const artifact = await writeArtifact(fileName, bytes, SPREADSHEET_MIME_TYPE, context);
3047
+ return {
3048
+ ok: true,
3049
+ content: [{
3050
+ type: "json",
3051
+ data: {
3052
+ path: artifact.path,
3053
+ size: artifact.size,
3054
+ ...summarize(spec)
3055
+ }
3056
+ }]
3057
+ };
3058
+ } catch (e) {
3059
+ return toolError(e);
3060
+ }
3061
+ }
3062
+ };
3063
+ }
3064
+
3065
+ //#endregion
3066
+ export { 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, DataSourceCandidatePolicy, DataSourcePolicy, DelegationOrchestrator, DownloadedFilePolicy, EXPORT_SPREADSHEET_TOOL, 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, 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, TODO_SYSTEM_PROMPT, TabWorksetPolicy, TodoStore, asReadMode, clampDownloadWaitMs, clampWorksetMax, createDelegationToolSource, createDocumentToolSource, createDownloadedFileToolSource, createHttpDataSourceTransport, createPageActionToolSource, createPagePerceptionToolSource, createSkillGenerationToolSource, createSpreadsheetToolSource, createTabWorksetToolSource, createTodoToolSource, defersImage, frameLabel, frameSteps, isPdfTextTrustworthy, measurePdfTextQuality, sniffDocumentFormat, takesImage, toActionFrameScopes, toFrameScopes, withDelegationOrigin };