@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.
Files changed (33) hide show
  1. package/dist/agent.d.ts +3 -3
  2. package/dist/agent.js +2 -2
  3. package/dist/browser.d.ts +3 -3
  4. package/dist/browser.js +7 -3
  5. package/dist/{catalogComponents-BFoqpT1v-CjUBZ3bc.js → catalogComponents-BgAJN0p8-C3K8klJd.js} +260 -914
  6. package/dist/{dist-qnlI2Iup.js → dist-DU9KDAuR.js} +194 -6
  7. package/dist/{dist-DTHZS2k1.js → dist-DqcL6jKO.js} +155 -39
  8. package/dist/{dist-B-cOu08W.js → dist-sdKFgERo.js} +431 -40
  9. package/dist/{echarts-DhNm2ene.js → echarts-De78wXqV.js} +599 -61
  10. package/dist/governance.d.ts +3 -3
  11. package/dist/{index-DACk2_XZ.d.ts → index-3fCHc1mQ.d.ts} +92 -8
  12. package/dist/{index-D3mONFHD.d.ts → index-C9pzXLKy.d.ts} +148 -4
  13. package/dist/{index-DWbs58LF.d.ts → index-DFhU1uks.d.ts} +64 -10
  14. package/dist/index.d.ts +3 -3
  15. package/dist/index.js +2 -2
  16. package/dist/mcp.d.ts +26 -3
  17. package/dist/mcp.js +37 -1
  18. package/dist/node.d.ts +3 -3
  19. package/dist/node.js +9 -3
  20. package/dist/{openUiLibrary-D5u8oIvx-BLOAQCho.js → openUiLibrary-BKXW7Iwx-DaymVubt.js} +3 -3
  21. package/dist/processSandboxEntry.js +2 -1
  22. package/dist/sandboxWorkerEntry.js +2 -1
  23. package/dist/{skillVersionStore-D-qHk9ZE-BcmFLykd.d.ts → skillVersionStore-D-qHk9ZE-DheTIwAB.d.ts} +1 -1
  24. package/dist/testing.d.ts +1 -1
  25. package/dist/{types-C26b05fW-CdrRCRDb.d.ts → types-DLctJep_-B5G4uk2u.d.ts} +3 -2
  26. package/dist/ui-react.d.ts +13 -4
  27. package/dist/ui-react.js +69 -253
  28. package/dist/ui-vue.d.ts +1 -1
  29. package/dist/ui-vue.js +1 -1
  30. package/dist/ui.d.ts +4 -4
  31. package/dist/ui.js +3 -3
  32. package/dist/{webskillLitCatalog-DME6PBkV-CmYNLlIT.js → webskillLitCatalog-D_zCqeQF-C9lrvvMr.js} +2 -2
  33. 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-B-cOu08W.js";
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: { preview: previewOf(draft) }
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"];