@webskill/sdk 0.11.0 → 0.12.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 +2 -2
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +7 -3
- package/dist/{catalogComponents-BFoqpT1v-CjUBZ3bc.js → catalogComponents-BgAJN0p8-C3K8klJd.js} +260 -914
- package/dist/{dist-qnlI2Iup.js → dist-DU9KDAuR.js} +194 -6
- package/dist/{dist-DTHZS2k1.js → dist-DqcL6jKO.js} +155 -39
- package/dist/{dist-B-cOu08W.js → dist-sdKFgERo.js} +431 -40
- package/dist/{echarts-DhNm2ene.js → echarts-De78wXqV.js} +599 -61
- package/dist/governance.d.ts +3 -3
- package/dist/{index-DACk2_XZ.d.ts → index-3fCHc1mQ.d.ts} +92 -8
- package/dist/{index-D3mONFHD.d.ts → index-C9pzXLKy.d.ts} +148 -4
- package/dist/{index-DWbs58LF.d.ts → index-DFhU1uks.d.ts} +64 -10
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/mcp.d.ts +26 -3
- package/dist/mcp.js +37 -1
- package/dist/node.d.ts +3 -3
- package/dist/node.js +9 -3
- package/dist/{openUiLibrary-D5u8oIvx-BLOAQCho.js → openUiLibrary-BKXW7Iwx-DaymVubt.js} +3 -3
- package/dist/processSandboxEntry.js +2 -1
- package/dist/sandboxWorkerEntry.js +2 -1
- package/dist/{skillVersionStore-D-qHk9ZE-BcmFLykd.d.ts → skillVersionStore-D-qHk9ZE-DheTIwAB.d.ts} +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{types-C26b05fW-CdrRCRDb.d.ts → types-DLctJep_-B5G4uk2u.d.ts} +3 -2
- package/dist/ui-react.d.ts +13 -4
- package/dist/ui-react.js +69 -253
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui-vue.js +1 -1
- package/dist/ui.d.ts +4 -4
- package/dist/ui.js +3 -3
- package/dist/{webskillLitCatalog-DME6PBkV-CmYNLlIT.js → webskillLitCatalog-D_zCqeQF-C9lrvvMr.js} +2 -2
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { M as messageOf, P as parseSkillMarkdown, g as assertRemoteUrlAllowed, h as WebSkillError, k as isValidSkillName } from "./dist-Bev6i6Ip.js";
|
|
2
|
-
import { f as DEFAULT_MAX_DATA_SOURCE_BYTES } from "./dist-
|
|
2
|
+
import { f as DEFAULT_MAX_DATA_SOURCE_BYTES } from "./dist-sdKFgERo.js";
|
|
3
3
|
|
|
4
4
|
//#region ../agent/dist/index.js
|
|
5
5
|
const STATUSES = [
|
|
@@ -108,7 +108,7 @@ const TODO_SYSTEM_PROMPT = [
|
|
|
108
108
|
"2. `update` an item to `in-progress` right before you start it, and to `completed` right after it succeeds.",
|
|
109
109
|
"3. Only one item may be `in-progress`; the runtime demotes the others automatically, so never rely on marking several.",
|
|
110
110
|
"4. Never mark an item `completed` before its work actually succeeded. If a step fails, keep it `in-progress` and explain.",
|
|
111
|
-
"5. `clear` the list once the whole request is answered, or when the user abandons it.",
|
|
111
|
+
"5. Settle every remaining item first (rule 4 still applies), then `clear` the list once the whole request is answered, or when the user abandons it.",
|
|
112
112
|
"",
|
|
113
113
|
"Skip the list for single-step requests, plain questions and trivial lookups — it only adds noise there."
|
|
114
114
|
].join("\n");
|
|
@@ -259,6 +259,7 @@ function createTodoToolSource(options = {}) {
|
|
|
259
259
|
inputSchema: INPUT_SCHEMA$4
|
|
260
260
|
}]),
|
|
261
261
|
canHandle: (name) => name === MANAGE_TODO_TOOL,
|
|
262
|
+
argCaptureTrust: (name) => name === "manage_todo" ? { tier: "reviewed" } : void 0,
|
|
262
263
|
call
|
|
263
264
|
};
|
|
264
265
|
}
|
|
@@ -313,6 +314,69 @@ function previewOf(draft) {
|
|
|
313
314
|
return sections.join("\n\n");
|
|
314
315
|
}
|
|
315
316
|
/**
|
|
317
|
+
* 工具名硬校(FR-30.4 / AC-30.8)。
|
|
318
|
+
*
|
|
319
|
+
* 只做**集合包含**:不比次数、不比顺序(AC-30.9),也不比参数(AC-30.10)。
|
|
320
|
+
* 模型把参数泛化成占位符是正常的技能写法,机械比对会把正确的技能判失败。
|
|
321
|
+
* 代价见需求 §7:工具名对、参数编得不对的情形本层拦不住,由确认卡兜底。
|
|
322
|
+
*/
|
|
323
|
+
function assertStepsAreBackedByTrace(draft, records) {
|
|
324
|
+
const claimed = draft.steps ?? [];
|
|
325
|
+
if (claimed.length === 0) return;
|
|
326
|
+
const executed = new Set(records.map((r) => r.tool));
|
|
327
|
+
const unknown = [...new Set(claimed.map((s) => s.tool).filter((tool) => !executed.has(tool)))];
|
|
328
|
+
if (unknown.length === 0) return;
|
|
329
|
+
fail(`The skill claims tools that were never called in this session: ${unknown.join(", ")}. Tools actually called: ${executed.size === 0 ? "(none)" : [...executed].sort().join(", ")}. Rewrite the skill using only the tools above.`, {
|
|
330
|
+
claimed: unknown,
|
|
331
|
+
executed: [...executed].sort()
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* 超限步骤拒收(FR-30.2 / AC-30.3)。
|
|
336
|
+
*
|
|
337
|
+
* 留存超过体积上限时 `args` 是空的,只剩一个工具名。放它过去等于产出一个
|
|
338
|
+
* 「知道调了什么、不知道传了什么」的步骤——那比没有这一步更糟,因为它看起来是完整的。
|
|
339
|
+
* 同名工具只要还有一条没超限的记录就不算超限:那条记录足以说明参数长什么样。
|
|
340
|
+
*/
|
|
341
|
+
function assertStepsAreNotTruncated(draft, records) {
|
|
342
|
+
const claimed = draft.steps ?? [];
|
|
343
|
+
if (claimed.length === 0) return;
|
|
344
|
+
const usable = new Set(records.filter((r) => r.truncated !== true).map((r) => r.tool));
|
|
345
|
+
const truncated = [...new Set(claimed.map((s) => s.tool).filter((tool) => !usable.has(tool)))];
|
|
346
|
+
if (truncated.length === 0) return;
|
|
347
|
+
fail(`The arguments of these tool calls were too large to record, so the skill cannot state them: ${truncated.join(", ")}. Rewrite the skill without these steps.`, { truncated });
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* 确认卡证据(AC-30.12 / AC-30.13):逐条列出模型**写进技能的**步骤,
|
|
351
|
+
* 并标注它是否有轨迹背书。轨迹里有而模型没写的步骤不出现在这里,
|
|
352
|
+
* 也不会被补进 `SKILL.md`——生成的是模型的技能,不是会话的录像(AC-30.11)。
|
|
353
|
+
*
|
|
354
|
+
* 校验通过后每一步都有背书,所以 `'model'` 只会出现在**轨迹读不到**时:
|
|
355
|
+
* 那种情况下不能把存储故障当成「模型在撞骗」而拦截,但必须在卡上说清楚
|
|
356
|
+
* 这些步骤一步都没背书,否则用户会把未校验的草稿当成已核实的。
|
|
357
|
+
*/
|
|
358
|
+
function buildTraceEvidence(draft, records) {
|
|
359
|
+
const claimed = draft.steps ?? [];
|
|
360
|
+
if (claimed.length === 0) return void 0;
|
|
361
|
+
const byTool = /* @__PURE__ */ new Map();
|
|
362
|
+
for (const record of records ?? []) {
|
|
363
|
+
if (record.truncated === true || byTool.has(record.tool)) continue;
|
|
364
|
+
byTool.set(record.tool, record);
|
|
365
|
+
}
|
|
366
|
+
return { steps: claimed.map((step) => {
|
|
367
|
+
const record = byTool.get(step.tool);
|
|
368
|
+
return {
|
|
369
|
+
tool: step.tool,
|
|
370
|
+
evidence: record === void 0 ? "model" : "trace",
|
|
371
|
+
...record !== void 0 ? {
|
|
372
|
+
traceArgs: record.args,
|
|
373
|
+
redacted: record.redacted
|
|
374
|
+
} : {},
|
|
375
|
+
...step.args !== void 0 ? { draftArgs: step.args } : {}
|
|
376
|
+
};
|
|
377
|
+
}) };
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
316
380
|
* 技能自动生成策略。生成动作是策略而不是内核:它不进入 `AgentLoop`,
|
|
317
381
|
* 而是经既有的 `ExternalToolSource` 扩展点接入(设计 02 §0)。
|
|
318
382
|
*
|
|
@@ -337,18 +401,27 @@ var SkillGenerator = class {
|
|
|
337
401
|
if (!sink) throw new WebSkillError("SKILL_GENERATION_DISABLED", "Skill generation is enabled but this host did not provide a candidate store");
|
|
338
402
|
if (this.#used >= this.#maxPerSession) throw new WebSkillError("SKILL_GENERATION_LIMIT_EXCEEDED", `This session already generated ${this.#maxPerSession} skills, which is the configured limit`);
|
|
339
403
|
validateDraft(draft);
|
|
404
|
+
const sessionId = this.#options.sessionId?.();
|
|
405
|
+
const trace = await this.#readSteps(sessionId);
|
|
406
|
+
if (trace.available && trace.records !== void 0) {
|
|
407
|
+
assertStepsAreBackedByTrace(draft, trace.records);
|
|
408
|
+
assertStepsAreNotTruncated(draft, trace.records);
|
|
409
|
+
}
|
|
340
410
|
this.#used += 1;
|
|
411
|
+
const evidence = trace.available ? buildTraceEvidence(draft, trace.records) : void 0;
|
|
341
412
|
this.#requestSeq += 1;
|
|
342
413
|
const response = await this.#options.ui.request({
|
|
343
414
|
type: "authorize",
|
|
344
415
|
id: `skill-generation-${this.#requestSeq}`,
|
|
345
416
|
capability: "confirm",
|
|
346
417
|
message: this.#options.messages?.confirmSkill?.(draft) ?? defaultConfirmSkill(draft),
|
|
347
|
-
details: {
|
|
418
|
+
details: {
|
|
419
|
+
preview: previewOf(draft),
|
|
420
|
+
...evidence !== void 0 ? { traceEvidence: evidence } : {}
|
|
421
|
+
}
|
|
348
422
|
});
|
|
349
423
|
if (response.cancelled === true || response.value === false) return { status: "declined" };
|
|
350
424
|
const confirmedAt = (this.#options.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
351
|
-
const sessionId = this.#options.sessionId?.();
|
|
352
425
|
const { id } = await sink.submit({
|
|
353
426
|
draft,
|
|
354
427
|
confirmedAt,
|
|
@@ -360,6 +433,24 @@ var SkillGenerator = class {
|
|
|
360
433
|
name: draft.name
|
|
361
434
|
};
|
|
362
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* 读不到轨迹(存储故障 / 没有会话号)时 `records` 为 `undefined`,含义是**不校验**,
|
|
438
|
+
* 而不是空数组(= 全部判失败):存储故障不该表现成「模型在撒谎」。
|
|
439
|
+
* 真正跑了零个工具的会话由 `[]` 表达,那种情况该拦就拦。
|
|
440
|
+
*/
|
|
441
|
+
async #readSteps(sessionId) {
|
|
442
|
+
const reader = this.#options.steps;
|
|
443
|
+
if (reader === void 0) return { available: false };
|
|
444
|
+
if (sessionId === void 0) return { available: true };
|
|
445
|
+
try {
|
|
446
|
+
return {
|
|
447
|
+
available: true,
|
|
448
|
+
records: await reader.listBySession(sessionId)
|
|
449
|
+
};
|
|
450
|
+
} catch {
|
|
451
|
+
return { available: true };
|
|
452
|
+
}
|
|
453
|
+
}
|
|
363
454
|
};
|
|
364
455
|
/**
|
|
365
456
|
* 技能自动生成的系统提示词。默认不注册工具,因此这段提示词只在
|
|
@@ -419,6 +510,25 @@ const INPUT_SCHEMA$3 = {
|
|
|
419
510
|
required: ["path", "content"],
|
|
420
511
|
additionalProperties: false
|
|
421
512
|
}
|
|
513
|
+
},
|
|
514
|
+
steps: {
|
|
515
|
+
type: "array",
|
|
516
|
+
description: "The tool calls this skill performs, in the order the SKILL.md describes them",
|
|
517
|
+
items: {
|
|
518
|
+
type: "object",
|
|
519
|
+
properties: {
|
|
520
|
+
tool: {
|
|
521
|
+
type: "string",
|
|
522
|
+
description: "The exact tool name, as it appeared in this conversation"
|
|
523
|
+
},
|
|
524
|
+
args: {
|
|
525
|
+
type: "object",
|
|
526
|
+
description: "The arguments this step passes to the tool"
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
required: ["tool"],
|
|
530
|
+
additionalProperties: false
|
|
531
|
+
}
|
|
422
532
|
}
|
|
423
533
|
},
|
|
424
534
|
required: [
|
|
@@ -441,11 +551,14 @@ function toolError$3(code, message) {
|
|
|
441
551
|
function readDraft(args) {
|
|
442
552
|
const { name, description, content } = args;
|
|
443
553
|
if (typeof name !== "string" || typeof description !== "string" || typeof content !== "string") return "The \"name\", \"description\" and \"content\" arguments are required and must be strings";
|
|
554
|
+
const steps = readSteps(args["steps"]);
|
|
555
|
+
if (typeof steps === "string") return steps;
|
|
444
556
|
const rawFiles = args["files"];
|
|
445
557
|
if (rawFiles === void 0) return {
|
|
446
558
|
name,
|
|
447
559
|
description,
|
|
448
|
-
content
|
|
560
|
+
content,
|
|
561
|
+
...steps
|
|
449
562
|
};
|
|
450
563
|
if (!Array.isArray(rawFiles)) return "The \"files\" argument must be an array";
|
|
451
564
|
const files = [];
|
|
@@ -462,9 +575,28 @@ function readDraft(args) {
|
|
|
462
575
|
name,
|
|
463
576
|
description,
|
|
464
577
|
content,
|
|
465
|
-
files
|
|
578
|
+
files,
|
|
579
|
+
...steps
|
|
466
580
|
};
|
|
467
581
|
}
|
|
582
|
+
function readSteps(raw) {
|
|
583
|
+
if (raw === void 0) return {};
|
|
584
|
+
if (!Array.isArray(raw)) return "The \"steps\" argument must be an array";
|
|
585
|
+
const steps = [];
|
|
586
|
+
for (const entry of raw) {
|
|
587
|
+
if (typeof entry !== "object" || entry === null) return "Every entry in \"steps\" must be an object";
|
|
588
|
+
const record = entry;
|
|
589
|
+
const tool = record["tool"];
|
|
590
|
+
if (typeof tool !== "string" || tool.trim() === "") return "Every step needs a non-empty \"tool\" name";
|
|
591
|
+
const stepArgs = record["args"];
|
|
592
|
+
if (stepArgs !== void 0 && (typeof stepArgs !== "object" || stepArgs === null || Array.isArray(stepArgs))) return "A step's \"args\" must be an object";
|
|
593
|
+
steps.push({
|
|
594
|
+
tool,
|
|
595
|
+
...stepArgs !== void 0 ? { args: stepArgs } : {}
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
return { steps };
|
|
599
|
+
}
|
|
468
600
|
/**
|
|
469
601
|
* 把技能自动生成接到既有的工具协议上(设计 02 §1.3)。
|
|
470
602
|
*
|
|
@@ -511,6 +643,7 @@ function createSkillGenerationToolSource(options) {
|
|
|
511
643
|
inputSchema: INPUT_SCHEMA$3
|
|
512
644
|
}]),
|
|
513
645
|
canHandle: (name) => name === GENERATE_SKILL_TOOL,
|
|
646
|
+
argCaptureTrust: (name) => name === "generate_skill" ? { tier: "reviewed" } : void 0,
|
|
514
647
|
call
|
|
515
648
|
};
|
|
516
649
|
}
|
|
@@ -713,6 +846,7 @@ function createDelegationToolSource(options) {
|
|
|
713
846
|
inputSchema: INPUT_SCHEMA$2
|
|
714
847
|
}]),
|
|
715
848
|
canHandle: (name) => name === DELEGATE_TASK_TOOL,
|
|
849
|
+
argCaptureTrust: (name) => name === "delegate_task" ? { tier: "reviewed" } : void 0,
|
|
716
850
|
call
|
|
717
851
|
};
|
|
718
852
|
}
|
|
@@ -894,6 +1028,7 @@ function createPagePerceptionToolSource(options) {
|
|
|
894
1028
|
}] : []),
|
|
895
1029
|
systemPrompt: () => Promise.resolve(policy.enabled ? PERCEPTION_SYSTEM_PROMPT : void 0),
|
|
896
1030
|
canHandle: (name) => name === PERCEIVE_PAGE_TOOL,
|
|
1031
|
+
argCaptureTrust: (name) => name === "perceive_page" ? { tier: "reviewed" } : void 0,
|
|
897
1032
|
call: async () => {
|
|
898
1033
|
try {
|
|
899
1034
|
const budget = await options.imageCapture?.();
|
|
@@ -1082,6 +1217,8 @@ const PAGE_ACTION_KINDS = [
|
|
|
1082
1217
|
"attach"
|
|
1083
1218
|
];
|
|
1084
1219
|
const declineReason = "Page action was declined by the user.";
|
|
1220
|
+
/** 目标记忆的上限;它在 `act()` 之后立刻就被读走,容量只为防无界增长 */
|
|
1221
|
+
const TARGET_MEMO_LIMIT = 200;
|
|
1085
1222
|
/**
|
|
1086
1223
|
* 页面操作策略(需求 23)。
|
|
1087
1224
|
*
|
|
@@ -1093,6 +1230,7 @@ var PageActionPolicy = class {
|
|
|
1093
1230
|
#options;
|
|
1094
1231
|
#records = [];
|
|
1095
1232
|
#seq = 0;
|
|
1233
|
+
#targetByRef = /* @__PURE__ */ new Map();
|
|
1096
1234
|
constructor(options) {
|
|
1097
1235
|
this.#options = options;
|
|
1098
1236
|
}
|
|
@@ -1107,6 +1245,35 @@ var PageActionPolicy = class {
|
|
|
1107
1245
|
get records() {
|
|
1108
1246
|
return this.#records;
|
|
1109
1247
|
}
|
|
1248
|
+
/**
|
|
1249
|
+
* 目标是不是密码类控件(分册 30)。与确认卡上把值打成掩码用的是**同一个判定**,
|
|
1250
|
+
* 不另立一套敏感字段清单。
|
|
1251
|
+
*
|
|
1252
|
+
* 读的是 `act()` 当时记下的结论而不是事后重查:提交或重绘后句柄会失效,
|
|
1253
|
+
* 重查就会把一次普通填写误判成敏感、把内容丢掉。没操作过的句柄才现查,
|
|
1254
|
+
* 查不到按敏感:不知道就别留存。
|
|
1255
|
+
*/
|
|
1256
|
+
isSecretTarget(ref) {
|
|
1257
|
+
const remembered = this.#targetByRef.get(ref);
|
|
1258
|
+
if (remembered !== void 0) return remembered.secret === true;
|
|
1259
|
+
const target = this.#options.executor.describe(ref);
|
|
1260
|
+
return target === void 0 || target.secret === true;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* `act()` 当时看到的目标身份(分册 30)。句柄只在本次感知内有效,
|
|
1264
|
+
* 只留句柄等于留了一个换次运行就失效的引用;角色与可访问名才是可重放的那部分。
|
|
1265
|
+
*/
|
|
1266
|
+
rememberedTarget(ref) {
|
|
1267
|
+
return this.#targetByRef.get(ref);
|
|
1268
|
+
}
|
|
1269
|
+
#rememberTarget(ref, target) {
|
|
1270
|
+
this.#targetByRef.delete(ref);
|
|
1271
|
+
this.#targetByRef.set(ref, target);
|
|
1272
|
+
if (this.#targetByRef.size > TARGET_MEMO_LIMIT) {
|
|
1273
|
+
const oldest = this.#targetByRef.keys().next();
|
|
1274
|
+
if (oldest.done !== true) this.#targetByRef.delete(oldest.value);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1110
1277
|
async act(request) {
|
|
1111
1278
|
if (!this.enabled) throw new WebSkillError("PAGE_ACTION_OUT_OF_SCOPE", "Page actions are not enabled: the host declared no actionable regions.");
|
|
1112
1279
|
const target = this.#options.executor.describe(request.ref);
|
|
@@ -1120,6 +1287,7 @@ var PageActionPolicy = class {
|
|
|
1120
1287
|
}, false);
|
|
1121
1288
|
throw new WebSkillError("PAGE_ACTION_DECLINED", declineReason);
|
|
1122
1289
|
}
|
|
1290
|
+
this.#rememberTarget(request.ref, target);
|
|
1123
1291
|
const outcome = await this.#options.executor.execute(request);
|
|
1124
1292
|
await this.#record(request, outcome, approved);
|
|
1125
1293
|
return outcome;
|
|
@@ -1242,6 +1410,26 @@ function createPageActionToolSource(options) {
|
|
|
1242
1410
|
}] : []),
|
|
1243
1411
|
systemPrompt: () => Promise.resolve(policy.enabled ? PAGE_ACTION_SYSTEM_PROMPT : void 0),
|
|
1244
1412
|
canHandle: (name) => name === PAGE_ACTION_TOOL,
|
|
1413
|
+
argCaptureTrust: (name, args) => {
|
|
1414
|
+
if (name !== "act_on_page") return void 0;
|
|
1415
|
+
const ref = args["ref"];
|
|
1416
|
+
if (typeof ref !== "string") return { tier: "untrusted" };
|
|
1417
|
+
return policy.isSecretTarget(ref) ? { tier: "untrusted" } : { tier: "reviewed" };
|
|
1418
|
+
},
|
|
1419
|
+
captureArgs: (name, args) => {
|
|
1420
|
+
if (name !== "act_on_page") return void 0;
|
|
1421
|
+
const ref = args["ref"];
|
|
1422
|
+
const target = typeof ref === "string" ? policy.rememberedTarget(ref) : void 0;
|
|
1423
|
+
if (target === void 0) return void 0;
|
|
1424
|
+
return {
|
|
1425
|
+
...args,
|
|
1426
|
+
target: {
|
|
1427
|
+
role: target.role,
|
|
1428
|
+
...target.name !== void 0 ? { name: target.name } : {},
|
|
1429
|
+
...target.frame !== void 0 ? { frame: target.frame } : {}
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
},
|
|
1245
1433
|
call: async (_name, args) => {
|
|
1246
1434
|
const ref = args["ref"];
|
|
1247
1435
|
const action = args["action"];
|