@webskill/sdk 0.5.0 → 0.7.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 +2 -2
- package/dist/agent.js +274 -42
- package/dist/browser.d.ts +127 -5
- package/dist/browser.js +611 -17
- package/dist/{catalogComponents-DV7cPpUm-C77AEEx9.js → catalogComponents-Dr5dFMAb-DKH_7VPI.js} +848 -974
- package/dist/{dist-6C03DShK.js → dist-D0qW6e40.js} +1213 -210
- package/dist/{dist-bewtXYlO.js → dist-DnYG2-eY.js} +53 -14
- package/dist/{env--jJB-TSX-04klhTYi.js → env-8cY40DXB-CGnEVZby.js} +7 -6
- package/dist/{env-BPUBZCwJ-4jat_SVG.d.ts → env-AK3cSMEA-Dli6QU5E.d.ts} +4 -3
- package/dist/eventTypes-DjIQpt8Y-Bj3vghj4.js +32 -0
- package/dist/governance.d.ts +78 -11
- package/dist/governance.js +173 -50
- package/dist/{index-D_7ZZjkl.d.ts → index-Ba3xFtfz.d.ts} +222 -8
- package/dist/{index-Bsqg4ftU.d.ts → index-BwsK9lGk.d.ts} +13 -7
- package/dist/{index-vBz_FC9w.d.ts → index-DkbABR43.d.ts} +336 -52
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +159 -11
- package/dist/mcp.js +276 -45
- package/dist/{memoryArtifactStore-BtOeB_hm-tj3fC5ip.js → memoryArtifactStore-52Zn9npI-BMPYwvoy.js} +10 -2
- package/dist/node.d.ts +4 -4
- package/dist/node.js +11 -3
- package/dist/{openUiLibrary-W3Ce896k-ClFTRZFs.js → openUiLibrary-Bdrji9qK-D2LxmM-a.js} +3 -3
- package/dist/{skillVersionStore-BzLbzFOL-CxwIewHJ.d.ts → skillVersionStore-Bl-ElD45-CWPvGvoq.d.ts} +8 -2
- package/dist/{testing-DDCJWvgA.js → testing-CYTFqkDm.js} +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +3 -3
- package/dist/{types-D_hoCri8-BnNPiZCi.d.ts → types-CcxRLdJG-DCXyw1US.d.ts} +62 -10
- package/dist/ui-react.d.ts +5 -3
- package/dist/ui-react.js +72 -47
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui-vue.js +13 -8
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +2 -2
- package/dist/{webskillLitCatalog-_mugzRHx-DiuJpCuf.js → webskillLitCatalog-_mugzRHx-B_54vxum.js} +1 -1
- package/package.json +1 -1
package/dist/governance.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { O as messageOf, T as isValidSkillName, g as assertSafePathSegment, m as WebSkillError } from "./dist-8oQRa8Xz.js";
|
|
2
|
-
import {
|
|
2
|
+
import { a as textParts, n as partsToText } from "./memoryArtifactStore-52Zn9npI-BMPYwvoy.js";
|
|
3
|
+
import { n as AUDIT_EVENT_TYPE_LIST, t as AUDIT_EVENT_TYPES } from "./eventTypes-DjIQpt8Y-Bj3vghj4.js";
|
|
3
4
|
|
|
4
5
|
//#region ../governance/dist/index.js
|
|
5
6
|
const invalid = (message, details) => {
|
|
@@ -331,19 +332,32 @@ function canonical(event) {
|
|
|
331
332
|
});
|
|
332
333
|
}
|
|
333
334
|
/**
|
|
335
|
+
* 同一进程内按审计文件路径共享的链式 mutex。
|
|
336
|
+
* 跟着实例走不行:本类允许多实例指向同一个 root,而交错追加正是分叉的来源。
|
|
337
|
+
*/
|
|
338
|
+
const APPEND_LOCKS = /* @__PURE__ */ new Map();
|
|
339
|
+
/**
|
|
334
340
|
* JSONL 追加式审计日志(<managedRoot>/.webskill/audit.jsonl):
|
|
335
|
-
* 真追加(fs.appendText,不整读改写)+ lastHash
|
|
341
|
+
* 真追加(fs.appendText,不整读改写)+ lastHash 缓存(仅当文件字节数未变时可用)。
|
|
336
342
|
* 语义:**tamper-evident, not tamper-proof**——verifyChain 能检出篡改/删除/换序,
|
|
337
343
|
* 但不阻止有写权限者直接改写文件;更强保证需要签名信任体系(未排期)。
|
|
344
|
+
*
|
|
345
|
+
* **并发边界**:追加的串行化只在**同一进程内**成立(模块级 mutex)。
|
|
346
|
+
* 跨进程 / 跨标签页靠的是「写前用 `stat().size` 重新确认链尾」:
|
|
347
|
+
* 外部写入会改变字节数,后写者因此会重读链尾而不会分叉——
|
|
348
|
+
* 除非两个进程落在同一个 stat/append 窗口内。真正的跨进程互斥需要文件锁,
|
|
349
|
+
* `FileSystemProvider` 不提供,不在本版承诺范围。
|
|
338
350
|
*/
|
|
339
351
|
var FsAuditLog = class {
|
|
340
352
|
#root;
|
|
341
353
|
#fs;
|
|
342
354
|
#now;
|
|
343
355
|
#createId;
|
|
344
|
-
/** lastHash
|
|
356
|
+
/** lastHash 内存缓存(仅当 #lastKnownSize 与当前文件字节数相等时才作数) */
|
|
345
357
|
#lastHash;
|
|
346
358
|
#lastHashSeeded = false;
|
|
359
|
+
/** 本实例最后一次写入后观察到的文件字节数 */
|
|
360
|
+
#lastKnownSize;
|
|
347
361
|
constructor(deps) {
|
|
348
362
|
this.#root = deps.root.replace(/\/+$/, "");
|
|
349
363
|
this.#fs = deps.fs;
|
|
@@ -354,8 +368,13 @@ var FsAuditLog = class {
|
|
|
354
368
|
async #seedLastHash() {
|
|
355
369
|
this.#lastHashSeeded = true;
|
|
356
370
|
const path = fileOf$1(this.#root);
|
|
357
|
-
if (!await this.#fs.exists(path))
|
|
358
|
-
|
|
371
|
+
if (!await this.#fs.exists(path)) {
|
|
372
|
+
this.#lastKnownSize = void 0;
|
|
373
|
+
return "GENESIS";
|
|
374
|
+
}
|
|
375
|
+
const text = await this.#fs.readText(path);
|
|
376
|
+
this.#lastKnownSize = await this.#fileSize();
|
|
377
|
+
const lines = text.split("\n").filter((l) => l.trim() !== "");
|
|
359
378
|
if (lines.length === 0) return "GENESIS";
|
|
360
379
|
try {
|
|
361
380
|
const last = JSON.parse(lines.at(-1));
|
|
@@ -364,21 +383,50 @@ var FsAuditLog = class {
|
|
|
364
383
|
throw new WebSkillError("GOVERNANCE_FAILED", `Audit log tail line at ${path} is corrupted; refusing to append (the chain must not silently restart)`, e);
|
|
365
384
|
}
|
|
366
385
|
}
|
|
386
|
+
/** 当前字节数;文件不存在、提供方不报 size 或 stat 抛错时统一返回 undefined */
|
|
387
|
+
async #fileSize() {
|
|
388
|
+
try {
|
|
389
|
+
return (await this.#fs.stat(fileOf$1(this.#root))).size;
|
|
390
|
+
} catch {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* 追加前解析链尾(FR-13.1)。
|
|
396
|
+
* 缓存只在「文件字节数与本实例上次写后一致」时才作数;
|
|
397
|
+
* 拿不到 size 就无条件重读——不把「大概没变」当正确性依据。
|
|
398
|
+
*/
|
|
399
|
+
async #resolvePrevHash() {
|
|
400
|
+
if (!this.#lastHashSeeded) return this.#seedLastHash();
|
|
401
|
+
const size = await this.#fileSize();
|
|
402
|
+
if (size === void 0 || size !== this.#lastKnownSize) return this.#seedLastHash();
|
|
403
|
+
return this.#lastHash;
|
|
404
|
+
}
|
|
405
|
+
/** 同一审计文件上的追加串行化(前一个失败也要继续排队) */
|
|
406
|
+
async #withAppendLock(work) {
|
|
407
|
+
const path = fileOf$1(this.#root);
|
|
408
|
+
const next = (APPEND_LOCKS.get(path) ?? Promise.resolve()).then(work, work);
|
|
409
|
+
APPEND_LOCKS.set(path, next.catch(() => void 0));
|
|
410
|
+
return next;
|
|
411
|
+
}
|
|
367
412
|
async append(event) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
413
|
+
return this.#withAppendLock(async () => {
|
|
414
|
+
const prevHash = await this.#resolvePrevHash();
|
|
415
|
+
const full = {
|
|
416
|
+
id: event.id ?? this.#createId?.() ?? `audit-${Math.random().toString(36).slice(2, 10)}`,
|
|
417
|
+
ts: event.ts ?? this.#now?.() ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
418
|
+
type: event.type,
|
|
419
|
+
target: event.target,
|
|
420
|
+
...event.actor !== void 0 ? { actor: event.actor } : {},
|
|
421
|
+
...event.data !== void 0 ? { data: event.data } : {},
|
|
422
|
+
prevHash
|
|
423
|
+
};
|
|
424
|
+
full.hash = await sha256Hex$1(canonical(full));
|
|
425
|
+
await this.#fs.appendText(fileOf$1(this.#root), `${JSON.stringify(full)}\n`);
|
|
426
|
+
this.#lastHash = full.hash;
|
|
427
|
+
this.#lastKnownSize = await this.#fileSize();
|
|
428
|
+
return full;
|
|
429
|
+
});
|
|
382
430
|
}
|
|
383
431
|
/**
|
|
384
432
|
* 分页 + 筛选下推。整文件读仍在(JSONL 布局未改,备案 D25),
|
|
@@ -425,29 +473,47 @@ var FsAuditLog = class {
|
|
|
425
473
|
}
|
|
426
474
|
const start = Math.max(0, end - limit);
|
|
427
475
|
const items = [];
|
|
476
|
+
let broken;
|
|
428
477
|
for (const line of matched.slice(start, end)) {
|
|
429
478
|
const event = records[line];
|
|
430
|
-
await this.#
|
|
479
|
+
const failure = await this.#chainFailureAt(records, line);
|
|
480
|
+
if (failure !== void 0) {
|
|
481
|
+
if (filter.tolerateBrokenChain !== true) throw new WebSkillError("GOVERNANCE_FAILED", `Audit chain is broken at line ${line} of ${path}: ${failure}`);
|
|
482
|
+
broken ??= {
|
|
483
|
+
at: line,
|
|
484
|
+
reason: failure
|
|
485
|
+
};
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
431
488
|
items.push(event);
|
|
432
489
|
}
|
|
433
490
|
return {
|
|
434
491
|
items,
|
|
435
|
-
...start > 0 ? { nextCursor: String(start) } : {}
|
|
492
|
+
...start > 0 ? { nextCursor: String(start) } : {},
|
|
493
|
+
...broken !== void 0 ? {
|
|
494
|
+
chainBrokenAt: broken.at,
|
|
495
|
+
chainReason: broken.reason
|
|
496
|
+
} : {}
|
|
436
497
|
};
|
|
437
498
|
}
|
|
438
|
-
/** 单行的链自洽:本行 hash 重算相符,且 prevHash 指向前一行的 hash */
|
|
439
|
-
async #
|
|
499
|
+
/** 单行的链自洽:本行 hash 重算相符,且 prevHash 指向前一行的 hash。自洽时返回 undefined */
|
|
500
|
+
async #chainFailureAt(records, index) {
|
|
440
501
|
const event = records[index];
|
|
441
502
|
const expectedPrev = index === 0 ? "GENESIS" : records[index - 1]?.hash;
|
|
442
|
-
if (expectedPrev === void 0 || event.prevHash !== expectedPrev)
|
|
443
|
-
if (event.hash !== await sha256Hex$1(canonical(event)))
|
|
503
|
+
if (expectedPrev === void 0 || event.prevHash !== expectedPrev) return "prevHash link mismatch (events may have been removed, reordered or corrupted)";
|
|
504
|
+
if (event.hash !== await sha256Hex$1(canonical(event))) return "event hash mismatch (event content was tampered)";
|
|
444
505
|
}
|
|
445
|
-
/** hash 链完整性校验:逐行重算 hash 并核对 prevHash
|
|
506
|
+
/** hash 链完整性校验:逐行重算 hash 并核对 prevHash 链接。**只读**,绝不改写历史 */
|
|
446
507
|
async verifyChain() {
|
|
447
508
|
const path = fileOf$1(this.#root);
|
|
448
|
-
if (!await this.#fs.exists(path)) return {
|
|
509
|
+
if (!await this.#fs.exists(path)) return {
|
|
510
|
+
ok: true,
|
|
511
|
+
total: 0
|
|
512
|
+
};
|
|
449
513
|
const lines = (await this.#fs.readText(path)).split("\n").filter((l) => l.trim() !== "");
|
|
514
|
+
const total = lines.length;
|
|
450
515
|
let expectedPrev = "GENESIS";
|
|
516
|
+
let prevTs;
|
|
451
517
|
for (let i = 0; i < lines.length; i++) {
|
|
452
518
|
let event;
|
|
453
519
|
try {
|
|
@@ -456,23 +522,29 @@ var FsAuditLog = class {
|
|
|
456
522
|
return {
|
|
457
523
|
ok: false,
|
|
458
524
|
brokenAt: i,
|
|
459
|
-
reason: "line is not valid JSON"
|
|
525
|
+
reason: "line is not valid JSON",
|
|
526
|
+
total,
|
|
527
|
+
...prevTs !== void 0 ? { brokenBeforeTs: prevTs } : {}
|
|
460
528
|
};
|
|
461
529
|
}
|
|
462
|
-
|
|
530
|
+
const broken = (reason) => ({
|
|
463
531
|
ok: false,
|
|
464
532
|
brokenAt: i,
|
|
465
|
-
reason
|
|
466
|
-
|
|
533
|
+
reason,
|
|
534
|
+
total,
|
|
535
|
+
...prevTs !== void 0 ? { brokenBeforeTs: prevTs } : {},
|
|
536
|
+
...typeof event.ts === "string" ? { brokenAfterTs: event.ts } : {}
|
|
537
|
+
});
|
|
538
|
+
if (event.prevHash !== expectedPrev) return broken("prevHash link mismatch (events may have been removed or reordered)");
|
|
467
539
|
const expectedHash = await sha256Hex$1(canonical(event));
|
|
468
|
-
if (event.hash !== expectedHash) return
|
|
469
|
-
ok: false,
|
|
470
|
-
brokenAt: i,
|
|
471
|
-
reason: "event hash mismatch (event content was tampered)"
|
|
472
|
-
};
|
|
540
|
+
if (event.hash !== expectedHash) return broken("event hash mismatch (event content was tampered)");
|
|
473
541
|
expectedPrev = event.hash;
|
|
542
|
+
prevTs = event.ts;
|
|
474
543
|
}
|
|
475
|
-
return {
|
|
544
|
+
return {
|
|
545
|
+
ok: true,
|
|
546
|
+
total
|
|
547
|
+
};
|
|
476
548
|
}
|
|
477
549
|
};
|
|
478
550
|
const dirOf = (root, skillName) => {
|
|
@@ -560,7 +632,7 @@ var SkillVersionStore = class {
|
|
|
560
632
|
async rollback(skillName, targetVersionId, input) {
|
|
561
633
|
const target = await this.get(skillName, targetVersionId);
|
|
562
634
|
const auditEvent = await this.#audit?.append({
|
|
563
|
-
type:
|
|
635
|
+
type: AUDIT_EVENT_TYPES.skillRolledBack,
|
|
564
636
|
target: skillName,
|
|
565
637
|
...input.actor !== void 0 ? { actor: input.actor } : {},
|
|
566
638
|
data: { targetVersionId }
|
|
@@ -653,6 +725,21 @@ var RepairPlanner = class {
|
|
|
653
725
|
};
|
|
654
726
|
const fileOf = (root) => `${root}/.webskill/states.json`;
|
|
655
727
|
/**
|
|
728
|
+
* 用户策略主动拒绝的错误码(FR-12.4):这是配置生效的证据,不是技能有问题,不计入隔离阈值。
|
|
729
|
+
*
|
|
730
|
+
* 集中在这一份常量里而不散在条件判断里,是为了让「哪些码不计数」只有一个事实源。
|
|
731
|
+
*/
|
|
732
|
+
const POLICY_DENIAL_CODES = /* @__PURE__ */ new Set([
|
|
733
|
+
"NETWORK_BLOCKED",
|
|
734
|
+
"TOOL_NOT_ALLOWED",
|
|
735
|
+
"FS_PERMISSION_DENIED"
|
|
736
|
+
]);
|
|
737
|
+
/** 自动隔离的原因判别式(FR-12.6);人工隔离仍写自由文本 */
|
|
738
|
+
const QUARANTINE_REASONS = {
|
|
739
|
+
integrity: "integrity",
|
|
740
|
+
failures: "failures"
|
|
741
|
+
};
|
|
742
|
+
/**
|
|
656
743
|
* 降级治理:states.json 持久化;失败达阈值 → quarantined;
|
|
657
744
|
* canRoute 仅 active;canExecute active|deprecated;disabled 执行抛 SKILL_DISABLED;
|
|
658
745
|
* 卸载/状态变更经 UiBridge confirm 审批。
|
|
@@ -713,17 +800,22 @@ var SkillStatePolicy = class {
|
|
|
713
800
|
data.states[skillName] = state;
|
|
714
801
|
if (state === "active") delete data.failures[skillName];
|
|
715
802
|
await this.#persist();
|
|
803
|
+
const auditData = {
|
|
804
|
+
...input.reason !== void 0 ? { reason: input.reason } : {},
|
|
805
|
+
...input.detail !== void 0 ? { detail: input.detail } : {}
|
|
806
|
+
};
|
|
716
807
|
await this.#audit?.append({
|
|
717
808
|
type: `skill.${state === "active" ? "reactivated" : state === "quarantined" ? "quarantined" : state === "deprecated" ? "deprecated" : "disabled"}`,
|
|
718
809
|
target: skillName,
|
|
719
810
|
...input.actor !== void 0 ? { actor: input.actor } : {},
|
|
720
|
-
...
|
|
811
|
+
...Object.keys(auditData).length > 0 ? { data: auditData } : {}
|
|
721
812
|
});
|
|
722
813
|
}
|
|
723
814
|
/**
|
|
724
815
|
* 失败计数;达阈值(默认 3)→ quarantined + 审计。
|
|
725
816
|
*
|
|
726
|
-
*
|
|
817
|
+
* 计数是**连续**而非累计(0.7.0 FR-12.5):一次成功执行经 `clearFailures` 归零,
|
|
818
|
+
* `setState(name, 'active')` 同样清零。
|
|
727
819
|
*/
|
|
728
820
|
async recordFailure(skillName, input = {}) {
|
|
729
821
|
assertSafePathSegment(skillName, "skill name");
|
|
@@ -735,10 +827,11 @@ var SkillStatePolicy = class {
|
|
|
735
827
|
state = "quarantined";
|
|
736
828
|
data.states[skillName] = "quarantined";
|
|
737
829
|
await this.#audit?.append({
|
|
738
|
-
type:
|
|
830
|
+
type: AUDIT_EVENT_TYPES.skillQuarantined,
|
|
739
831
|
target: skillName,
|
|
740
832
|
...input.actor !== void 0 ? { actor: input.actor } : {},
|
|
741
833
|
data: {
|
|
834
|
+
reason: QUARANTINE_REASONS.failures,
|
|
742
835
|
failures: count,
|
|
743
836
|
threshold: this.#failureThreshold
|
|
744
837
|
}
|
|
@@ -747,6 +840,17 @@ var SkillStatePolicy = class {
|
|
|
747
840
|
await this.#persist();
|
|
748
841
|
return state;
|
|
749
842
|
}
|
|
843
|
+
/**
|
|
844
|
+
* 成功清零(FR-12.5)。不写审计(成功是常态,写了会把审计流淹掉),
|
|
845
|
+
* 也**不改变 state**——已隔离的技能不会因一次成功自动解除。
|
|
846
|
+
*/
|
|
847
|
+
async clearFailures(skillName) {
|
|
848
|
+
assertSafePathSegment(skillName, "skill name");
|
|
849
|
+
const data = await this.#load();
|
|
850
|
+
if (data.failures[skillName] === void 0) return;
|
|
851
|
+
delete data.failures[skillName];
|
|
852
|
+
await this.#persist();
|
|
853
|
+
}
|
|
750
854
|
canRoute(state) {
|
|
751
855
|
return state === "active";
|
|
752
856
|
}
|
|
@@ -790,25 +894,44 @@ var SkillStatePolicy = class {
|
|
|
790
894
|
reason: `integrity check failed: ${e instanceof Error ? e.message : String(e)}`
|
|
791
895
|
};
|
|
792
896
|
}
|
|
793
|
-
if (!verdict.ok) await this.setState(skillName, "quarantined", {
|
|
897
|
+
if (!verdict.ok) await this.setState(skillName, "quarantined", {
|
|
898
|
+
reason: QUARANTINE_REASONS.integrity,
|
|
899
|
+
detail: verdict.reason ?? "integrity verification failed on activation"
|
|
900
|
+
});
|
|
794
901
|
return verdict;
|
|
795
902
|
} };
|
|
796
903
|
}
|
|
797
904
|
/**
|
|
798
905
|
* runtime `SkillOutcomeReporter` 适配(F1):一次真实的技能脚本执行失败 → `recordFailure`,
|
|
799
|
-
* 达阈值即自动 `quarantined` +
|
|
906
|
+
* 达阈值即自动 `quarantined` + 写审计;一次成功 → `clearFailures`。
|
|
800
907
|
*
|
|
801
908
|
* 0.4.0 之前 `recordFailure` 没有任何生产调用方——`states.json` 的失败计数只在单测里增长,
|
|
802
909
|
* 于是「失败达阈值自动隔离」在真实部署里从不触发,Console 上那个计数恒为 0。
|
|
803
910
|
* 这个适配器就是把那一段接上:runtime 不能反向依赖治理,装配点只能在宿主。
|
|
804
911
|
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
912
|
+
* 策略拒绝的分流放在这里而不是 `recordFailure`:后者是**治理动作**(管理员也可手工调用),
|
|
913
|
+
* 它不该知道运行时错误码;判定“这次失败算不算技能的错”是**上报侧**的语义。
|
|
807
914
|
*/
|
|
808
915
|
toSkillOutcomeReporter(input = {}) {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
916
|
+
const denied = input.policyDenialCodes ?? POLICY_DENIAL_CODES;
|
|
917
|
+
const actor = input.actor !== void 0 ? { actor: input.actor } : {};
|
|
918
|
+
return {
|
|
919
|
+
onSkillFailed: async (report) => {
|
|
920
|
+
if (denied.has(report.code)) {
|
|
921
|
+
await this.#audit?.append({
|
|
922
|
+
type: AUDIT_EVENT_TYPES.skillPolicyDenied,
|
|
923
|
+
target: report.skillName,
|
|
924
|
+
...actor,
|
|
925
|
+
data: { code: report.code }
|
|
926
|
+
});
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
await this.recordFailure(report.skillName, actor);
|
|
930
|
+
},
|
|
931
|
+
onSkillSucceeded: async (report) => {
|
|
932
|
+
await this.clearFailures(report.skillName);
|
|
933
|
+
}
|
|
934
|
+
};
|
|
812
935
|
}
|
|
813
936
|
/** disabled → SKILL_DISABLED;quarantined → SKILL_QUARANTINED */
|
|
814
937
|
async assertExecutable(skillName) {
|
|
@@ -839,7 +962,7 @@ var SkillStatePolicy = class {
|
|
|
839
962
|
delete data.failures[skillName];
|
|
840
963
|
await this.#persist();
|
|
841
964
|
await this.#audit?.append({
|
|
842
|
-
type:
|
|
965
|
+
type: AUDIT_EVENT_TYPES.skillUninstalled,
|
|
843
966
|
target: skillName,
|
|
844
967
|
actor: input.actor
|
|
845
968
|
});
|
|
@@ -1089,4 +1212,4 @@ function createMissHook(deps) {
|
|
|
1089
1212
|
}
|
|
1090
1213
|
|
|
1091
1214
|
//#endregion
|
|
1092
|
-
export { AUDIT_PAGE_SIZE, AlwaysHumanApprovalPolicy, CANDIDATE_PAGE_SIZE, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, EvaluationRunner, FailureAnalyzer, FsAuditLog, LlmCandidateGenerator, RepairPlanner, SCORING_WEIGHTS, SKILL_VERSION_PAGE_SIZE, SimilarityDetector, SkillScorer, SkillStatePolicy, SkillVersionStore, candidateToCatalogEntry, completeText, createCandidateSink, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };
|
|
1215
|
+
export { AUDIT_EVENT_TYPES, AUDIT_EVENT_TYPE_LIST, AUDIT_PAGE_SIZE, AlwaysHumanApprovalPolicy, CANDIDATE_PAGE_SIZE, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, EvaluationRunner, FailureAnalyzer, FsAuditLog, LlmCandidateGenerator, POLICY_DENIAL_CODES, QUARANTINE_REASONS, RepairPlanner, SCORING_WEIGHTS, SKILL_VERSION_PAGE_SIZE, SimilarityDetector, SkillScorer, SkillStatePolicy, SkillVersionStore, candidateToCatalogEntry, completeText, createCandidateSink, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as InteractionOrigin,
|
|
2
|
-
import {
|
|
1
|
+
import { a as InteractionOrigin, c as InteractionResponse, s as InteractionRequest, x as UiBridge } from "./types-CcxRLdJG-DCXyw1US.js";
|
|
2
|
+
import { k as ExternalToolSource } from "./index-DkbABR43.js";
|
|
3
3
|
//#region ../agent/dist/index.d.ts
|
|
4
4
|
//#region src/todo/types.d.ts
|
|
5
5
|
/** 待办条目状态:未开始 / 进行中 / 已完成(FR-3.1) */
|
|
@@ -165,6 +165,9 @@ declare function createSkillGenerationToolSource(options: SkillGenerationToolSou
|
|
|
165
165
|
/**
|
|
166
166
|
* 技能自动生成的系统提示词。默认不注册工具,因此这段提示词只在
|
|
167
167
|
* 宿主打开开关时才进入上下文(设计 02 §1.3)。
|
|
168
|
+
*
|
|
169
|
+
* 触发条件写成正面清单而不是「不要自发调用」(0.6.0 FR-18.5):实测中后者会连
|
|
170
|
+
* 「帮我写一份流程规范」这类明确的沉淀请求一起抑制掉,开关等于白开。
|
|
168
171
|
*/
|
|
169
172
|
declare const SKILL_GENERATION_SYSTEM_PROMPT: string;
|
|
170
173
|
//#endregion
|
|
@@ -312,8 +315,52 @@ interface PerceivedNode {
|
|
|
312
315
|
name?: string;
|
|
313
316
|
/** 仅非敏感控件的当前值 */
|
|
314
317
|
value?: string;
|
|
318
|
+
/** 关联的图像分片 id(FR-12.1);节点里只放 id 不放 base64,否则 JSON 树会被截断切碎 */
|
|
319
|
+
imageId?: string;
|
|
320
|
+
/** 取像失败的降级说明(英文);不得静默丢弃(FR-12.1) */
|
|
321
|
+
imageNote?: string;
|
|
322
|
+
/**
|
|
323
|
+
* 不透明元素句柄(S10)。**仅当该节点可被操作时出现**:
|
|
324
|
+
* 它落在宿主声明的操作范围内、且是可交互角色。
|
|
325
|
+
* 由 reader 生成,只在产生它的那次感知内有效;模型不得构造或推断。
|
|
326
|
+
*/
|
|
327
|
+
ref?: string;
|
|
315
328
|
children?: readonly PerceivedNode[];
|
|
316
329
|
}
|
|
330
|
+
/** 随感知一起下发的一张图像(FR-12.1) @experimental */
|
|
331
|
+
interface PerceivedImage {
|
|
332
|
+
/** 与 `PerceivedNode.imageId` 对应 */
|
|
333
|
+
id: string;
|
|
334
|
+
mimeType: string;
|
|
335
|
+
/** base64,不含 `data:` 前缀 */
|
|
336
|
+
data: string;
|
|
337
|
+
/** 取像级别:`src` 为原图字节,`canvas` 为画布/SVG 读回 */
|
|
338
|
+
level: 'src' | 'canvas';
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* 取像预算与开关(FR-12.3 / FR-12.5)。
|
|
342
|
+
* 它只能来自宿主配置与当前模型能力,**不包含任何范围字段**:
|
|
343
|
+
* “读哪里”仍只由构造时的 `scope` 决定(AC-10.8)。
|
|
344
|
+
* @experimental
|
|
345
|
+
*/
|
|
346
|
+
interface PerceptionCaptureOptions {
|
|
347
|
+
/** false 时 reader 不得进入任何取像分支(抓完再丢弃不可接受) */
|
|
348
|
+
images: boolean;
|
|
349
|
+
maxImageBytes: number;
|
|
350
|
+
maxImages: number;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* reader 的完整产物。只实现文本的 reader 可以继续返回节点数组。
|
|
354
|
+
* @experimental
|
|
355
|
+
*/
|
|
356
|
+
interface PerceptionResult {
|
|
357
|
+
nodes: readonly PerceivedNode[];
|
|
358
|
+
images?: readonly PerceivedImage[];
|
|
359
|
+
/** 超出 `maxImages` 而未下发的张数 */
|
|
360
|
+
imagesOmitted?: number;
|
|
361
|
+
/** 取像失败的张数;失败不影响整次感知的成功与否 */
|
|
362
|
+
imageFailures?: number;
|
|
363
|
+
}
|
|
317
364
|
/**
|
|
318
365
|
* 页面读取实现的注入口(浏览器侧 `createDomPerceptionReader`)。
|
|
319
366
|
*
|
|
@@ -322,7 +369,7 @@ interface PerceivedNode {
|
|
|
322
369
|
* @experimental
|
|
323
370
|
*/
|
|
324
371
|
interface PagePerceptionReader {
|
|
325
|
-
read(scope: PerceptionScope): Promise<readonly PerceivedNode[]> | readonly PerceivedNode[];
|
|
372
|
+
read(scope: PerceptionScope, capture?: PerceptionCaptureOptions): Promise<readonly PerceivedNode[] | PerceptionResult> | readonly PerceivedNode[] | PerceptionResult;
|
|
326
373
|
}
|
|
327
374
|
/** 一次感知的留痕(FR-10.5 的 UI 提示与 FR-10.6 的审计共用同一条记录) @experimental */
|
|
328
375
|
interface PerceptionRecord {
|
|
@@ -333,19 +380,33 @@ interface PerceptionRecord {
|
|
|
333
380
|
exclude: readonly string[];
|
|
334
381
|
/** 读到的顶层节点数;用于「它读的比我预期的多」这类判断 */
|
|
335
382
|
nodeCount: number;
|
|
383
|
+
/** 本次下发的图像张数,按取像级别分组(只在取像开启时存在) */
|
|
384
|
+
images?: {
|
|
385
|
+
src: number;
|
|
386
|
+
canvas: number;
|
|
387
|
+
};
|
|
388
|
+
imagesOmitted?: number;
|
|
389
|
+
/** 读取失败的张数;藏起来会让用户误以为模型看到了全部图片 */
|
|
390
|
+
imageFailures?: number;
|
|
336
391
|
}
|
|
337
392
|
/**
|
|
338
|
-
*
|
|
393
|
+
* 页面侧留痕的统一出口(感知与操作共用)。
|
|
394
|
+
* 刻意只声明 `append` 的结构化子集,`FsAuditLog` 直接满足——
|
|
339
395
|
* agent 包不能依赖 governance,但审计不能因此变成「记在内存里」。
|
|
340
396
|
* @experimental
|
|
341
397
|
*/
|
|
342
|
-
interface
|
|
398
|
+
interface PageAuditSink {
|
|
343
399
|
append(event: {
|
|
344
400
|
type: string;
|
|
345
401
|
target: string;
|
|
346
402
|
data?: Record<string, unknown>;
|
|
347
403
|
}): Promise<unknown>;
|
|
348
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* @deprecated 改用 `PageAuditSink`。保留以兼容 0.5.0 的注入代码。
|
|
407
|
+
* @experimental
|
|
408
|
+
*/
|
|
409
|
+
type PerceptionAuditSink = PageAuditSink;
|
|
349
410
|
//#endregion
|
|
350
411
|
//#region src/perception/policy.d.ts
|
|
351
412
|
interface PagePerceptionPolicyOptions {
|
|
@@ -361,9 +422,10 @@ interface PagePerceptionPolicyOptions {
|
|
|
361
422
|
/**
|
|
362
423
|
* 页面只读感知策略(需求 10)。
|
|
363
424
|
*
|
|
364
|
-
* **`perceive()`
|
|
425
|
+
* **`perceive()` 不接受任何范围参数**——这是「白名单判定不受模型输出影响」
|
|
365
426
|
* (AC-10.8)的实现方式。不是先拿模型给的范围再去校验,而是模型压根没有
|
|
366
427
|
* 表达范围的入口:能被读的区域只由构造时的 `scope` 决定。
|
|
428
|
+
* 唯一的参数是取像预算(`PerceptionCaptureOptions`),它里面没有任何范围字段。
|
|
367
429
|
*
|
|
368
430
|
* 本类**没有**任何点击 / 输入 / 提交 / 导航 / 滚动方法(FR-10.7)。
|
|
369
431
|
* @experimental
|
|
@@ -378,8 +440,10 @@ declare class PagePerceptionPolicy {
|
|
|
378
440
|
get records(): readonly PerceptionRecord[];
|
|
379
441
|
/** FR-10.5:感知发生时通知 UI,chatbot 据此在消息流里标注一行 */
|
|
380
442
|
subscribe(listener: (record: PerceptionRecord) => void): () => void;
|
|
381
|
-
perceive(): Promise<{
|
|
443
|
+
perceive(capture?: PerceptionCaptureOptions): Promise<{
|
|
382
444
|
nodes: readonly PerceivedNode[];
|
|
445
|
+
images: readonly PerceivedImage[];
|
|
446
|
+
imagesOmitted: number;
|
|
383
447
|
record: PerceptionRecord;
|
|
384
448
|
}>;
|
|
385
449
|
}
|
|
@@ -388,6 +452,17 @@ declare class PagePerceptionPolicy {
|
|
|
388
452
|
declare const PERCEIVE_PAGE_TOOL = "perceive_page";
|
|
389
453
|
interface PagePerceptionToolSourceOptions {
|
|
390
454
|
policy: PagePerceptionPolicy;
|
|
455
|
+
/**
|
|
456
|
+
* 取像预算与开关(FR-12.5)。每次调用重取,设置或选中模型改完立刻生效;
|
|
457
|
+
* 缺省即不取像。判定在遍历 DOM **之前**完成一次,不是每个节点判一次。
|
|
458
|
+
*/
|
|
459
|
+
imageCapture?(): ImageCaptureBudget | Promise<ImageCaptureBudget>;
|
|
460
|
+
}
|
|
461
|
+
/** @experimental */
|
|
462
|
+
interface ImageCaptureBudget {
|
|
463
|
+
enabled: boolean;
|
|
464
|
+
maxImageBytes: number;
|
|
465
|
+
maxImages: number;
|
|
391
466
|
}
|
|
392
467
|
/**
|
|
393
468
|
* 把只读感知接到既有工具协议上。**本工具源不导出任何写入动作**(FR-10.7):
|
|
@@ -408,4 +483,143 @@ declare function createPagePerceptionToolSource(options: PagePerceptionToolSourc
|
|
|
408
483
|
*/
|
|
409
484
|
declare const PERCEPTION_SYSTEM_PROMPT: string;
|
|
410
485
|
//#endregion
|
|
411
|
-
|
|
486
|
+
//#region src/pageAction/types.d.ts
|
|
487
|
+
/**
|
|
488
|
+
* 页面操作的策略层类型(需求 23)。
|
|
489
|
+
*
|
|
490
|
+
* 与感知层同一条规矩:这一层不含任何 DOM 概念——执行落在 `@webskill/browser`,
|
|
491
|
+
* 本包只定义「什么允许被操作」以及一次操作长什么样。
|
|
492
|
+
*/
|
|
493
|
+
/**
|
|
494
|
+
* 可操作范围白名单(FR-23.1,硬约束)。
|
|
495
|
+
*
|
|
496
|
+
* **没有默认值,空 `include` 就是任何操作都不可执行。**
|
|
497
|
+
* 与 `PerceptionScope` 同构但**独立声明**:写成类型别名会让「感知与操作共用一份配置」
|
|
498
|
+
* 在类型层面合法,而 FR-23.1 写死了那是不可接受的——可读不等于可操作。
|
|
499
|
+
* @experimental
|
|
500
|
+
*/
|
|
501
|
+
interface PageActionScope {
|
|
502
|
+
/** 可操作的根节点选择器;未声明即不可操作 */
|
|
503
|
+
include: readonly string[];
|
|
504
|
+
/** 从 include 子树中剪除的危险控件(支付、删除、权限变更等) */
|
|
505
|
+
exclude?: readonly string[];
|
|
506
|
+
}
|
|
507
|
+
/** 本版的最小操作集(FR-23.4)。导航、拖拽、滚动均不在内。 @experimental */
|
|
508
|
+
type PageActionKind = 'click' | 'fill' | 'submit';
|
|
509
|
+
/** @experimental */
|
|
510
|
+
interface PageActionRequest {
|
|
511
|
+
/** 感知产出的不透明句柄;不接受选择器(FR-23.4) */
|
|
512
|
+
ref: string;
|
|
513
|
+
action: PageActionKind;
|
|
514
|
+
/** `action === 'fill'` 时必填 */
|
|
515
|
+
value?: string;
|
|
516
|
+
}
|
|
517
|
+
/** @experimental */
|
|
518
|
+
interface PageActionOutcome {
|
|
519
|
+
ok: boolean;
|
|
520
|
+
/**
|
|
521
|
+
* 实际操作到的元素的角色与可访问名称。
|
|
522
|
+
* 由**执行器**回填而不是调用方从感知结果里查:执行的那一刻元素可能已经变了,
|
|
523
|
+
* 留痕必须记实际操作到的东西(FR-23.3)。
|
|
524
|
+
*/
|
|
525
|
+
target: {
|
|
526
|
+
role: string;
|
|
527
|
+
name?: string;
|
|
528
|
+
};
|
|
529
|
+
/** 目标是密码类控件:确认卡与留痕据此隐去值(FR-23.3) */
|
|
530
|
+
secret?: boolean;
|
|
531
|
+
/** 失败原因(英文) */
|
|
532
|
+
reason?: string;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* 页面操作执行器。与 `PagePerceptionReader` 对称,同样不含 DOM 概念。
|
|
536
|
+
* 实现方负责校验句柄有效性、元素可交互性,以及目标是否仍在操作范围内。
|
|
537
|
+
* @experimental
|
|
538
|
+
*/
|
|
539
|
+
interface PageActionExecutor {
|
|
540
|
+
execute(request: PageActionRequest): Promise<PageActionOutcome> | PageActionOutcome;
|
|
541
|
+
/** 目标的角色与可访问名称,供确认卡在**执行前**说清将要做什么(FR-23.2) */
|
|
542
|
+
describe(ref: string): {
|
|
543
|
+
role: string;
|
|
544
|
+
name?: string;
|
|
545
|
+
secret?: boolean;
|
|
546
|
+
} | undefined;
|
|
547
|
+
}
|
|
548
|
+
/** 一次页面操作的留痕(FR-23.3 的五项字段) @experimental */
|
|
549
|
+
interface PageActionRecord {
|
|
550
|
+
/** ISO 8601 */
|
|
551
|
+
at: string;
|
|
552
|
+
action: PageActionKind;
|
|
553
|
+
role: string;
|
|
554
|
+
name?: string;
|
|
555
|
+
/** 是否经用户确认;宿主预授权的操作记 `'preauthorized'` */
|
|
556
|
+
approved: boolean | 'preauthorized';
|
|
557
|
+
ok: boolean;
|
|
558
|
+
/** 非密码类控件填入的值;密码类控件**整个字段不存在** */
|
|
559
|
+
value?: string;
|
|
560
|
+
reason?: string;
|
|
561
|
+
}
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/pageAction/policy.d.ts
|
|
564
|
+
/** 授权卡的最小出口;`UiBridge` 直接满足(与技能生成用的是同一个端口) */
|
|
565
|
+
interface PageActionUi {
|
|
566
|
+
request(input: InteractionRequest): Promise<InteractionResponse>;
|
|
567
|
+
}
|
|
568
|
+
interface PageActionPolicyOptions {
|
|
569
|
+
/** 宿主声明的可操作范围;`include` 为空即整个能力不可用 */
|
|
570
|
+
scope: PageActionScope;
|
|
571
|
+
executor: PageActionExecutor;
|
|
572
|
+
ui: PageActionUi;
|
|
573
|
+
/** FR-23.3:每次操作写审计。不注入即不留痕,装配方要自己承担这个选择 */
|
|
574
|
+
audit?: PageAuditSink;
|
|
575
|
+
/** 审计事件的 target(缺省 `page`) */
|
|
576
|
+
auditTarget?: string;
|
|
577
|
+
/**
|
|
578
|
+
* 免确认的操作类型。**缺省即每一次操作都要确认**(AC-23.4)。
|
|
579
|
+
* 只有宿主显式配置才生效,SDK 不提供任何缺省放行项。
|
|
580
|
+
*/
|
|
581
|
+
preauthorized?: readonly PageActionKind[];
|
|
582
|
+
now?(): string;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* 页面操作策略(需求 23)。
|
|
586
|
+
*
|
|
587
|
+
* 三道闸门依次是:宿主有没有声明范围、用户认不认、执行器认不认。
|
|
588
|
+
* 模型能表达的只有「在哪个句柄上做哪个动作」——它没有任何影响范围的入口(AC-23.2)。
|
|
589
|
+
* @experimental
|
|
590
|
+
*/
|
|
591
|
+
declare class PageActionPolicy {
|
|
592
|
+
#private;
|
|
593
|
+
constructor(options: PageActionPolicyOptions);
|
|
594
|
+
/** 白名单为空即不可用(FR-23.1):宿主没声明范围就没有这个能力 */
|
|
595
|
+
get enabled(): boolean;
|
|
596
|
+
get scope(): PageActionScope;
|
|
597
|
+
/** 最近的操作记录(只读展示用),新的在前 */
|
|
598
|
+
get records(): readonly PageActionRecord[];
|
|
599
|
+
act(request: PageActionRequest): Promise<PageActionOutcome>;
|
|
600
|
+
}
|
|
601
|
+
//#endregion
|
|
602
|
+
//#region src/pageAction/toolSource.d.ts
|
|
603
|
+
declare const PAGE_ACTION_TOOL = "act_on_page";
|
|
604
|
+
interface PageActionToolSourceOptions {
|
|
605
|
+
policy: PageActionPolicy;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* 把页面操作接到既有工具协议上。
|
|
609
|
+
*
|
|
610
|
+
* 策略未注入或宿主没声明可操作区域时 `listToolSpecs()` 返回空数组——
|
|
611
|
+
* 模型连这个工具的存在都看不到,而不是看得到再被拒(FR-23.5 / AC-23.8)。
|
|
612
|
+
* @experimental
|
|
613
|
+
*/
|
|
614
|
+
declare function createPageActionToolSource(options: PageActionToolSourceOptions): ExternalToolSource;
|
|
615
|
+
//#endregion
|
|
616
|
+
//#region src/prompts/pageAction.d.ts
|
|
617
|
+
/**
|
|
618
|
+
* 页面操作的系统提示词(需求 23)。
|
|
619
|
+
*
|
|
620
|
+
* 措辞刻意保守:提示词里的鼓励性表述会直接抬高模型尝试操作的频率,
|
|
621
|
+
* 而每一次尝试都要打断用户去点确认。
|
|
622
|
+
*/
|
|
623
|
+
declare const PAGE_ACTION_SYSTEM_PROMPT: string;
|
|
624
|
+
//#endregion
|
|
625
|
+
export { TodoListener as $, PagePerceptionReader as A, SKILL_GENERATION_SYSTEM_PROMPT as B, PageActionRequest as C, PageAuditSink as D, PageActionUi as E, PerceptionAuditSink as F, SkillGenerator as G, SkillGenerationOutcome as H, PerceptionCaptureOptions as I, SubAgentRunner as J, SkillGeneratorOptions as K, PerceptionRecord as L, ParentBudget as M, PerceivedImage as N, PagePerceptionPolicy as O, PerceivedNode as P, TodoList as Q, PerceptionResult as R, PageActionRecord as S, PageActionToolSourceOptions as T, SkillGenerationPolicy as U, SkillCandidateSink as V, SkillGenerationToolSourceOptions as W, TodoEvent as X, TODO_SYSTEM_PROMPT as Y, TodoItem as Z, PageActionExecutor as _, DelegationOrchestratorOptions as a, createPagePerceptionToolSource as at, PageActionPolicy as b, DelegationToolSourceOptions as c, withDelegationOrigin as ct, ImageCaptureBudget as d, TodoStatus as et, MANAGE_TODO_TOOL as f, PERCEPTION_SYSTEM_PROMPT as g, PERCEIVE_PAGE_TOOL as h, DelegationOrchestrator as i, createPageActionToolSource as it, PagePerceptionToolSourceOptions as j, PagePerceptionPolicyOptions as k, GENERATE_SKILL_TOOL as l, PAGE_ACTION_TOOL as m, DELEGATION_SYSTEM_PROMPT as n, TodoToolSourceOptions as nt, DelegationRequest as o, createSkillGenerationToolSource as ot, PAGE_ACTION_SYSTEM_PROMPT as p, SubAgentRunInput as q, DelegationBudget as r, createDelegationToolSource as rt, DelegationResult as s, createTodoToolSource as st, DELEGATE_TASK_TOOL as t, TodoStore as tt, GeneratedSkillDraft as u, PageActionKind as v, PageActionScope as w, PageActionPolicyOptions as x, PageActionOutcome as y, PerceptionScope as z };
|