@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.d.ts +3 -3
- package/dist/agent.js +277 -61
- package/dist/{approval-Bbh_Apwg.js → approval-DwN2o2QG.js} +433 -21
- package/dist/browser.d.ts +689 -4
- package/dist/browser.js +1637 -33
- package/dist/{eventTypes-CwciaqCU.js → eventTypes-DcTQXSV1.js} +7 -1
- package/dist/governance.d.ts +9 -3
- package/dist/governance.js +1 -1
- package/dist/{index-ad11wflI.d.ts → index-B8Tgmpwh.d.ts} +2 -2
- package/dist/{index-DQ3-6GFu.d.ts → index-CYLt5u4L.d.ts} +252 -3
- package/dist/{index-BDyXe-a5.d.ts → index-DHopiH_m.d.ts} +261 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +47 -41
- package/dist/{kind-DxgS8LM-.js → kind-DaKqLX2F.js} +3 -17
- package/dist/linkedDocument-C0gj1sMq.js +245 -0
- package/dist/mcp.d.ts +2 -2
- package/dist/node.d.ts +8 -3
- package/dist/node.js +79 -4
- package/dist/processSandboxEntry.js +5 -0
- package/dist/sandboxWorkerEntry.js +5 -0
- package/dist/{skillVersionStore-B24Jjjry-v2CCU0Xo.d.ts → skillVersionStore-B24Jjjry-BkOk9wI_.d.ts} +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/toolSource-C9oNuKmr.js +565 -0
- package/dist/{types-Btpdd1y--BcxQ10Fa.d.ts → types-BvTV_05--BW8zDuEk.d.ts} +54 -3
- package/dist/ui-react.d.ts +3 -3
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui.d.ts +3 -3
- package/dist/untrustedText-BIaRvPZK.js +16 -0
- package/dist/{webSkillApi-CsvA69qk.js → webSkillApi-Cib6G-94.js} +1 -1
- package/package.json +6 -5
- package/dist/linkedDocument-xNXF-z8n.js +0 -94
- 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 {
|
|
5
|
-
import { o as
|
|
6
|
-
import {
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
242
|
-
if (status !== void 0 && typeof status !== "string") return Promise.resolve(toolError$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
828
|
-
if (typeof task !== "string" || task.trim() === "") return toolError$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
1404
|
-
inputSchema: INPUT_SCHEMA$
|
|
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$
|
|
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$
|
|
1422
|
-
if (ref !== void 0 && ref !== pending.subtreeRef) return toolError$
|
|
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$
|
|
1485
|
-
return toolError$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
2218
|
-
if (action === "scroll" && value !== "down" && value !== "up") return toolError$
|
|
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$
|
|
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$
|
|
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$
|
|
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:
|
|
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
|
-
|
|
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 };
|