@webskill/sdk 0.2.5 → 0.2.7

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.
@@ -1,7 +1,7 @@
1
- import { B as SkillManifest, I as SkillDocument, P as SkillCatalogEntry, c as LlmClient, u as LlmMessage, v as UiBridge, w as FileSystemProvider } from "./types-CKm5G_eQ-krKWW8WV.js";
2
- import { Q as ScriptExecutor, dt as WebSkillRuntime, ft as WebSkillRuntimeDeps, q as RuntimeRun, tt as SkillStateGuard } from "./index-DfINBEOy.js";
3
- import { d as SkillManager } from "./index-CsDJvYGV.js";
4
- //#region ../governance/dist/index.d.ts
1
+ import { G as SkillDocument, N as FileSystemProvider, U as SkillCatalogEntry, Y as SkillManifest, c as LlmClient, u as LlmMessage, v as UiBridge } from "./types-7fnqDVrf-BnRQjVU3.js";
2
+ import { Q as ScriptExecutor, dt as WebSkillRuntime, ft as WebSkillRuntimeDeps, q as RuntimeRun, tt as SkillStateGuard } from "./index-BpIK7tJM.js";
3
+ import { d as SkillManager } from "./index-BHL5FWGw.js";
4
+ //#region ../governance/dist/documentSource-9dhqKIVQ.d.ts
5
5
  //#region src/types.d.ts
6
6
  type CandidateStatus = 'draft' | 'pending-review' | 'approved' | 'published' | 'rejected';
7
7
  type CandidateSource = 'runtime-miss' | 'document' | 'manual';
@@ -60,32 +60,6 @@ interface SkillVersion {
60
60
  }
61
61
  type SkillState = 'active' | 'quarantined' | 'deprecated' | 'disabled';
62
62
  //#endregion
63
- //#region src/candidate/candidateNormalizer.d.ts
64
- /** 剥 markdown fence 与 <think> 块、截取首尾 {};非对象 → CANDIDATE_INVALID */
65
- declare function parseJsonObject(raw: string): Record<string, unknown>;
66
- /** 小写连字符化、≤64、过 isValidSkillName */
67
- declare function sanitizeCandidateName(raw: string): string;
68
- /**
69
- * 文件归一:补 SKILL.md;路径含 `..` / 以 `/` 开头 / 含 `\` → CANDIDATE_INVALID;
70
- * scripts/ 下必须是 .ts/.js。
71
- */
72
- declare function normalizeCandidateFiles(files: Array<{
73
- path: string;
74
- content: string;
75
- }>, name: string, description: string): CandidateFile[];
76
- /** 含脚本且 LLM 自报 low → 强制 medium 并追加"必须人工审批"原因 */
77
- declare function normalizeRisk(declared: unknown, files: CandidateFile[], reasons: string[]): {
78
- risk: CandidateRisk;
79
- reasons: string[];
80
- };
81
- /** 不信任归一化管线全链路;状态恒为 draft */
82
- declare function normalizeCandidate(input: {
83
- raw: Record<string, unknown>;
84
- source: CandidateSource;
85
- now?: () => string;
86
- createId?: () => string;
87
- }): CandidateSkill;
88
- //#endregion
89
63
  //#region src/candidate/candidateValidator.d.ts
90
64
  /** 候选校验:缺 SKILL.md / 非法扩展名 → CANDIDATE_INVALID */
91
65
  declare function validateCandidate(candidate: CandidateSkill): void;
@@ -123,25 +97,6 @@ declare class LlmCandidateGenerator {
123
97
  }): Promise<CandidateSkill>;
124
98
  }
125
99
  //#endregion
126
- //#region src/approval/policies.d.ts
127
- interface ApprovalDecision {
128
- needsHuman: boolean;
129
- reason: string;
130
- }
131
- interface ApprovalPolicy {
132
- evaluate(candidate: CandidateSkill): ApprovalDecision;
133
- }
134
- /** 默认策略:任何候选都必须人工审批 */
135
- declare class AlwaysHumanApprovalPolicy implements ApprovalPolicy {
136
- evaluate(candidate: CandidateSkill): ApprovalDecision;
137
- }
138
- /** 规则组合策略:首个命中的规则胜出,全部未命中走 fallback(默认 AlwaysHuman) */
139
- declare class CompositeApprovalPolicy implements ApprovalPolicy {
140
- #private;
141
- constructor(rules: Array<(candidate: CandidateSkill) => ApprovalDecision | undefined>, fallback?: ApprovalPolicy);
142
- evaluate(candidate: CandidateSkill): ApprovalDecision;
143
- }
144
- //#endregion
145
100
  //#region src/versioning/skillVersionStore.d.ts
146
101
  /** 版本存储:manifest 快照 + parentVersionId 链;回滚 = 追加新版本(谱系不断)。
147
102
  * 保留策略:maxArchivesPerSkill(默认 5)超出时清理最旧版本(json + zip 归档一并删除)。 */
@@ -173,38 +128,6 @@ declare class SkillVersionStore {
173
128
  }): Promise<SkillVersion>;
174
129
  }
175
130
  //#endregion
176
- //#region src/approval/approvalWorkflow.d.ts
177
- /** 审批工作流:review(UiBridge confirm 真实接线)/ publish(校验→安装→版本→审计) */
178
- declare class ApprovalWorkflow {
179
- #private;
180
- constructor(deps: {
181
- policy: ApprovalPolicy;
182
- audit: AuditLog;
183
- store: CandidateStore;
184
- skillManager: SkillManager;
185
- versions: SkillVersionStore;
186
- fs?: FileSystemProvider;
187
- });
188
- /** 策略评估;needs-human 时经 UiBridge confirm 真实询问,按应答迁移状态 */
189
- review(candidateId: string, input: {
190
- actor: string;
191
- uiBridge?: UiBridge;
192
- }): Promise<CandidateSkill>;
193
- /** publish 全链路:approved 前置 → 写出 staging → validateSkills → install → 版本 → 审计 */
194
- publish(candidateId: string, input: {
195
- actor: string;
196
- }): Promise<SkillManifest>;
197
- /**
198
- * 真实回滚(受审批保护:仅经显式 actor 调用并全程审计):
199
- * 版本归档解包 → staging 校验 → 原子安装(复用安装管线 swap)→ 追加新版本 + 审计。
200
- * RepairPlanner 的 rollback 选项(targetVersionId)经本方法执行。
201
- */
202
- applyRollback(skillName: string, versionId: string, input: {
203
- actor: string;
204
- reason?: string;
205
- }): Promise<SkillManifest>;
206
- }
207
- //#endregion
208
131
  //#region src/audit/fsAuditLog.d.ts
209
132
  interface AuditChainVerification {
210
133
  ok: boolean;
@@ -365,18 +288,6 @@ declare class EvaluationRunner {
365
288
  run(tasks: EvaluationTask[]): Promise<EvaluationReport>;
366
289
  }
367
290
  //#endregion
368
- //#region src/evaluation/evaluationRuntime.d.ts
369
- /**
370
- * 治理评估专用 runtime 装配(不可信技能试用路径):
371
- * 默认注入 ProcessSandboxExecutor(fork + --permission 真实进程隔离;子进程
372
- * env 默认清空防密钥泄露,需透传时经 ProcessSandboxOptions.envWhitelist 显式放行)。
373
- * 可配置 executor 切回 SandboxedScriptExecutor(worker_threads 能力面收敛形态,
374
- * 非安全边界;envWhitelist 同样适用于该执行器)。
375
- */
376
- declare function createEvaluationRuntime(deps: WebSkillRuntimeDeps & {
377
- executor?: ScriptExecutor;
378
- }): WebSkillRuntime;
379
- //#endregion
380
291
  //#region src/evaluation/testSuggestion.d.ts
381
292
  /** 失败 trace → 回归评估任务建议(prompt 复现 + expected 错误模式) */
382
293
  declare function suggestFromFailedRun(run: RuntimeRun): EvaluationTask;
@@ -448,6 +359,96 @@ interface SourceDocument {
448
359
  /** 读文档 + sha256 hash(变更检测用) */
449
360
  declare function readDocument(fs: FileSystemProvider, path: string): Promise<SourceDocument>;
450
361
  //#endregion
362
+ //#region ../governance/dist/index.d.ts
363
+ //#region src/candidate/candidateNormalizer.d.ts
364
+ /** 剥 markdown fence 与 <think> 块、截取首尾 {};非对象 → CANDIDATE_INVALID */
365
+ declare function parseJsonObject(raw: string): Record<string, unknown>;
366
+ /** 小写连字符化、≤64、过 isValidSkillName */
367
+ declare function sanitizeCandidateName(raw: string): string;
368
+ /**
369
+ * 文件归一:补 SKILL.md;路径含 `..` / 以 `/` 开头 / 含 `\` → CANDIDATE_INVALID;
370
+ * scripts/ 下必须是 .ts/.js。
371
+ */
372
+ declare function normalizeCandidateFiles(files: Array<{
373
+ path: string;
374
+ content: string;
375
+ }>, name: string, description: string): CandidateFile[];
376
+ /** 含脚本且 LLM 自报 low → 强制 medium 并追加"必须人工审批"原因 */
377
+ declare function normalizeRisk(declared: unknown, files: CandidateFile[], reasons: string[]): {
378
+ risk: CandidateRisk;
379
+ reasons: string[];
380
+ };
381
+ /** 不信任归一化管线全链路;状态恒为 draft */
382
+ declare function normalizeCandidate(input: {
383
+ raw: Record<string, unknown>;
384
+ source: CandidateSource;
385
+ now?: () => string;
386
+ createId?: () => string;
387
+ }): CandidateSkill;
388
+ //#endregion
389
+ //#region src/approval/policies.d.ts
390
+ interface ApprovalDecision {
391
+ needsHuman: boolean;
392
+ reason: string;
393
+ }
394
+ interface ApprovalPolicy {
395
+ evaluate(candidate: CandidateSkill): ApprovalDecision;
396
+ }
397
+ /** 默认策略:任何候选都必须人工审批 */
398
+ declare class AlwaysHumanApprovalPolicy implements ApprovalPolicy {
399
+ evaluate(candidate: CandidateSkill): ApprovalDecision;
400
+ }
401
+ /** 规则组合策略:首个命中的规则胜出,全部未命中走 fallback(默认 AlwaysHuman) */
402
+ declare class CompositeApprovalPolicy implements ApprovalPolicy {
403
+ #private;
404
+ constructor(rules: Array<(candidate: CandidateSkill) => ApprovalDecision | undefined>, fallback?: ApprovalPolicy);
405
+ evaluate(candidate: CandidateSkill): ApprovalDecision;
406
+ }
407
+ //#endregion
408
+ //#region src/approval/approvalWorkflow.d.ts
409
+ /** 审批工作流:review(UiBridge confirm 真实接线)/ publish(校验→安装→版本→审计) */
410
+ declare class ApprovalWorkflow {
411
+ #private;
412
+ constructor(deps: {
413
+ policy: ApprovalPolicy;
414
+ audit: AuditLog;
415
+ store: CandidateStore;
416
+ skillManager: SkillManager;
417
+ versions: SkillVersionStore;
418
+ fs?: FileSystemProvider;
419
+ });
420
+ /** 策略评估;needs-human 时经 UiBridge confirm 真实询问,按应答迁移状态 */
421
+ review(candidateId: string, input: {
422
+ actor: string;
423
+ uiBridge?: UiBridge;
424
+ }): Promise<CandidateSkill>;
425
+ /** publish 全链路:approved 前置 → 写出 staging → validateSkills → install → 版本 → 审计 */
426
+ publish(candidateId: string, input: {
427
+ actor: string;
428
+ }): Promise<SkillManifest>;
429
+ /**
430
+ * 真实回滚(受审批保护:仅经显式 actor 调用并全程审计):
431
+ * 版本归档解包 → staging 校验 → 原子安装(复用安装管线 swap)→ 追加新版本 + 审计。
432
+ * RepairPlanner 的 rollback 选项(targetVersionId)经本方法执行。
433
+ */
434
+ applyRollback(skillName: string, versionId: string, input: {
435
+ actor: string;
436
+ reason?: string;
437
+ }): Promise<SkillManifest>;
438
+ }
439
+ //#endregion
440
+ //#region src/evaluation/evaluationRuntime.d.ts
441
+ /**
442
+ * 治理评估专用 runtime 装配(不可信技能试用路径):
443
+ * 默认注入 ProcessSandboxExecutor(fork + --permission 真实进程隔离;子进程
444
+ * env 默认清空防密钥泄露,需透传时经 ProcessSandboxOptions.envWhitelist 显式放行)。
445
+ * 可配置 executor 切回 SandboxedScriptExecutor(worker_threads 能力面收敛形态,
446
+ * 非安全边界;envWhitelist 同样适用于该执行器)。
447
+ */
448
+ declare function createEvaluationRuntime(deps: WebSkillRuntimeDeps & {
449
+ executor?: ScriptExecutor;
450
+ }): WebSkillRuntime;
451
+ //#endregion
451
452
  //#region src/documents/documentSkillExtractor.d.ts
452
453
  /** 文档 → LLM 抽取 → 完整防御管线 → draft 候选(source: document) */
453
454
  declare class DocumentSkillExtractor {
@@ -1,12 +1,11 @@
1
1
  import { C as messageOf, M as unzipWithLimits, N as validateSkills, f as assertSafePathSegment, j as resolveInsideRoot, u as WebSkillError, x as isValidSkillName } from "./dist-BQzncxXg.js";
2
- import { S as WebSkillRuntime } from "./dist-B77plHjw.js";
3
- import { i as NodeFS, s as ProcessSandboxExecutor, u as exportArchive } from "./dist-Chk8iB-E.js";
2
+ import { S as WebSkillRuntime } from "./dist-BdOW8N4V.js";
3
+ import { i as NodeFS, s as ProcessSandboxExecutor, u as exportArchive } from "./dist-CcMIUXeZ.js";
4
4
  import path from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  import { mkdtemp } from "node:fs/promises";
7
- import { createHash } from "node:crypto";
8
7
 
9
- //#region ../governance/dist/index.js
8
+ //#region ../governance/dist/documentSource-C6gq6pbk.js
10
9
  const invalid = (message, details) => {
11
10
  throw new WebSkillError("CANDIDATE_INVALID", message, details);
12
11
  };
@@ -217,180 +216,12 @@ var LlmCandidateGenerator = class {
217
216
  return candidate;
218
217
  }
219
218
  };
220
- /** 默认策略:任何候选都必须人工审批 */
221
- var AlwaysHumanApprovalPolicy = class {
222
- evaluate(candidate) {
223
- return {
224
- needsHuman: true,
225
- reason: `Candidate "${candidate.name}" requires human approval (risk: ${candidate.risk})`
226
- };
227
- }
228
- };
229
- /** 规则组合策略:首个命中的规则胜出,全部未命中走 fallback(默认 AlwaysHuman) */
230
- var CompositeApprovalPolicy = class {
231
- #rules;
232
- #fallback;
233
- constructor(rules, fallback) {
234
- this.#rules = rules;
235
- this.#fallback = fallback ?? new AlwaysHumanApprovalPolicy();
236
- }
237
- evaluate(candidate) {
238
- for (const rule of this.#rules) {
239
- const decision = rule(candidate);
240
- if (decision) return decision;
241
- }
242
- return this.#fallback.evaluate(candidate);
243
- }
244
- };
245
- /** 审批工作流:review(UiBridge confirm 真实接线)/ publish(校验→安装→版本→审计) */
246
- var ApprovalWorkflow = class {
247
- #policy;
248
- #audit;
249
- #store;
250
- #skillManager;
251
- #versions;
252
- #fs;
253
- constructor(deps) {
254
- this.#policy = deps.policy;
255
- this.#audit = deps.audit;
256
- this.#store = deps.store;
257
- this.#skillManager = deps.skillManager;
258
- this.#versions = deps.versions;
259
- this.#fs = deps.fs ?? new NodeFS();
260
- }
261
- /** 策略评估;needs-human 时经 UiBridge confirm 真实询问,按应答迁移状态 */
262
- async review(candidateId, input) {
263
- const candidate = await this.#store.get(candidateId);
264
- if (candidate.status !== "draft" && candidate.status !== "pending-review") throw new WebSkillError("GOVERNANCE_FAILED", `Candidate "${candidateId}" cannot be reviewed from status "${candidate.status}"`);
265
- const decision = this.#policy.evaluate(candidate);
266
- let approved;
267
- if (decision.needsHuman) {
268
- if (!input.uiBridge) {
269
- await this.#store.updateStatus(candidateId, "pending-review");
270
- throw new WebSkillError("APPROVAL_REQUIRED", `Candidate "${candidate.name}" requires human approval: ${decision.reason}`);
271
- }
272
- await this.#store.updateStatus(candidateId, "pending-review");
273
- const response = await input.uiBridge.request({
274
- type: "confirm",
275
- id: `approval-${candidateId}`,
276
- message: `Approve candidate "${candidate.name}" (risk: ${candidate.risk})? ${decision.reason}`,
277
- defaultValue: false
278
- });
279
- approved = response.cancelled !== true && response.value === true;
280
- } else approved = true;
281
- const updated = await this.#store.updateStatus(candidateId, approved ? "approved" : "rejected");
282
- await this.#audit.append({
283
- type: "candidate.reviewed",
284
- target: candidateId,
285
- actor: input.actor,
286
- data: {
287
- approved,
288
- reason: decision.reason
289
- }
290
- });
291
- return updated;
292
- }
293
- /** publish 全链路:approved 前置 → 写出 staging → validateSkills → install → 版本 → 审计 */
294
- async publish(candidateId, input) {
295
- const candidate = await this.#store.get(candidateId);
296
- if (candidate.status !== "approved") throw new WebSkillError("APPROVAL_REQUIRED", `Candidate "${candidate.name}" must be approved before publishing (status: ${candidate.status})`);
297
- const stagingRoot = (await mkdtemp(path.join(tmpdir(), "webskill-candidate-"))).split(path.sep).join("/");
298
- try {
299
- const skillDir = `${stagingRoot}/${candidate.name}`;
300
- for (const file of candidate.files) await this.#fs.writeText(resolveInsideRoot(skillDir, file.path), file.content);
301
- const report = await validateSkills(this.#fs, [stagingRoot]);
302
- if (!report.ok) {
303
- const errors = report.issues.filter((i) => i.severity === "error");
304
- throw new WebSkillError("GOVERNANCE_FAILED", `Candidate "${candidate.name}" failed validation: ${errors.map((i) => i.message).join("; ")}`, errors);
305
- }
306
- const manifest = await this.#skillManager.install({
307
- type: "local",
308
- path: skillDir
309
- });
310
- const archiveOut = `${stagingRoot}/version-archive.zip`;
311
- await exportArchive(this.#fs, `${this.#skillManager.managedRoot}/${candidate.name}`, {
312
- format: "zip",
313
- outPath: archiveOut
314
- });
315
- await this.#versions.add(candidate.name, {
316
- reason: `Publish candidate ${candidateId}`,
317
- manifest,
318
- archive: await this.#fs.readBinary(archiveOut)
319
- });
320
- await this.#store.updateStatus(candidateId, "published");
321
- await this.#audit.append({
322
- type: "skill.published",
323
- target: candidate.name,
324
- actor: input.actor,
325
- data: {
326
- candidateId,
327
- digest: manifest.integrity.digest
328
- }
329
- });
330
- return manifest;
331
- } catch (e) {
332
- if (e instanceof WebSkillError) throw e;
333
- throw new WebSkillError("GOVERNANCE_FAILED", `Failed to publish candidate "${candidateId}": ${messageOf(e)}`, e);
334
- } finally {
335
- try {
336
- await this.#fs.remove(stagingRoot, { recursive: true });
337
- } catch {}
338
- }
339
- }
340
- /**
341
- * 真实回滚(受审批保护:仅经显式 actor 调用并全程审计):
342
- * 版本归档解包 → staging 校验 → 原子安装(复用安装管线 swap)→ 追加新版本 + 审计。
343
- * RepairPlanner 的 rollback 选项(targetVersionId)经本方法执行。
344
- */
345
- async applyRollback(skillName, versionId, input) {
346
- assertSafePathSegment(skillName, "skill name");
347
- assertSafePathSegment(versionId, "version id");
348
- const version = await this.#versions.get(skillName, versionId);
349
- const archive = await this.#versions.readArchive(skillName, versionId);
350
- const stagingRoot = (await mkdtemp(path.join(tmpdir(), "webskill-rollback-"))).split(path.sep).join("/");
351
- try {
352
- const skillDir = `${stagingRoot}/${skillName}`;
353
- for (const [rel, content] of await unzipWithLimits(archive)) {
354
- if (rel.endsWith("/")) continue;
355
- await this.#fs.writeBinary(resolveInsideRoot(skillDir, rel), content);
356
- }
357
- const report = await validateSkills(this.#fs, [stagingRoot]);
358
- if (!report.ok) {
359
- const errors = report.issues.filter((i) => i.severity === "error");
360
- throw new WebSkillError("GOVERNANCE_FAILED", `Rollback archive of "${skillName}" failed validation: ${errors.map((i) => i.message).join("; ")}`, errors);
361
- }
362
- const manifest = await this.#skillManager.install({
363
- type: "local",
364
- path: skillDir
365
- });
366
- if (manifest.integrity.digest !== version.manifest.integrity.digest) throw new WebSkillError("GOVERNANCE_FAILED", `Rollback of "${skillName}" to version "${versionId}" produced a digest mismatch: expected ${version.manifest.integrity.digest}, got ${manifest.integrity.digest}`);
367
- await this.#versions.add(skillName, {
368
- reason: input.reason ?? `Rollback to version ${versionId}`,
369
- manifest,
370
- archive
371
- });
372
- await this.#audit.append({
373
- type: "skill.rolled_back",
374
- target: skillName,
375
- actor: input.actor,
376
- data: {
377
- targetVersionId: versionId,
378
- reason: input.reason
379
- }
380
- });
381
- return manifest;
382
- } catch (e) {
383
- if (e instanceof WebSkillError) throw e;
384
- throw new WebSkillError("GOVERNANCE_FAILED", `Failed to roll back "${skillName}" to version "${versionId}": ${messageOf(e)}`, e);
385
- } finally {
386
- try {
387
- await this.#fs.remove(stagingRoot, { recursive: true });
388
- } catch {}
389
- }
390
- }
391
- };
392
219
  const fileOf$1 = (root) => `${root}/.webskill/audit.jsonl`;
393
- const sha256Hex = (text) => createHash("sha256").update(text, "utf8").digest("hex");
220
+ /** 环境无关 sha256(WebCrypto;Node ≥17 与浏览器均有 globalThis.crypto.subtle) */
221
+ const sha256Hex$1 = async (text) => {
222
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
223
+ return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
224
+ };
394
225
  /** 链字段之外的规范化事件载荷(hash 计算输入) */
395
226
  function canonical(event) {
396
227
  return JSON.stringify({
@@ -432,7 +263,7 @@ var FsAuditLog = class {
432
263
  if (lines.length === 0) return "GENESIS";
433
264
  try {
434
265
  const last = JSON.parse(lines.at(-1));
435
- return last.hash ?? sha256Hex(canonical(last));
266
+ return last.hash ?? await sha256Hex$1(canonical(last));
436
267
  } catch (e) {
437
268
  throw new WebSkillError("GOVERNANCE_FAILED", `Audit log tail line at ${path} is corrupted; refusing to append (the chain must not silently restart)`, e);
438
269
  }
@@ -448,7 +279,7 @@ var FsAuditLog = class {
448
279
  ...event.data !== void 0 ? { data: event.data } : {},
449
280
  prevHash
450
281
  };
451
- full.hash = sha256Hex(canonical(full));
282
+ full.hash = await sha256Hex$1(canonical(full));
452
283
  await this.#fs.appendText(fileOf$1(this.#root), `${JSON.stringify(full)}\n`);
453
284
  this.#lastHash = full.hash;
454
285
  return full;
@@ -495,7 +326,7 @@ var FsAuditLog = class {
495
326
  brokenAt: i,
496
327
  reason: "prevHash link mismatch (events may have been removed or reordered)"
497
328
  };
498
- const expectedHash = sha256Hex(canonical(event));
329
+ const expectedHash = await sha256Hex$1(canonical(event));
499
330
  if (event.hash !== expectedHash) return {
500
331
  ok: false,
501
332
  brokenAt: i,
@@ -871,19 +702,6 @@ var EvaluationRunner = class {
871
702
  };
872
703
  }
873
704
  };
874
- /**
875
- * 治理评估专用 runtime 装配(不可信技能试用路径):
876
- * 默认注入 ProcessSandboxExecutor(fork + --permission 真实进程隔离;子进程
877
- * env 默认清空防密钥泄露,需透传时经 ProcessSandboxOptions.envWhitelist 显式放行)。
878
- * 可配置 executor 切回 SandboxedScriptExecutor(worker_threads 能力面收敛形态,
879
- * 非安全边界;envWhitelist 同样适用于该执行器)。
880
- */
881
- function createEvaluationRuntime(deps) {
882
- return new WebSkillRuntime({
883
- ...deps,
884
- executor: deps.executor ?? new ProcessSandboxExecutor(deps.fs)
885
- });
886
- }
887
705
  /** 失败 trace → 回归评估任务建议(prompt 复现 + expected 错误模式) */
888
706
  function suggestFromFailedRun(run) {
889
707
  const errorPatterns = run.trace.filter((e) => e.type === "tool.failed" || e.type === "run.failed").map((e) => String(e.data?.["code"] ?? e.message ?? "")).filter(Boolean);
@@ -977,15 +795,208 @@ var DependencyGraph = class DependencyGraph {
977
795
  return out.sort();
978
796
  }
979
797
  };
798
+ /** 环境无关 sha256(WebCrypto;Node ≥17 与浏览器均有 globalThis.crypto.subtle) */
799
+ async function sha256Hex(text) {
800
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
801
+ return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
802
+ }
980
803
  /** 读文档 + sha256 hash(变更检测用) */
981
804
  async function readDocument(fs, path) {
982
805
  const content = await fs.readText(path);
983
806
  return {
984
807
  path,
985
808
  content,
986
- hash: createHash("sha256").update(content).digest("hex")
809
+ hash: await sha256Hex(content)
987
810
  };
988
811
  }
812
+
813
+ //#endregion
814
+ //#region ../governance/dist/index.js
815
+ /** 默认策略:任何候选都必须人工审批 */
816
+ var AlwaysHumanApprovalPolicy = class {
817
+ evaluate(candidate) {
818
+ return {
819
+ needsHuman: true,
820
+ reason: `Candidate "${candidate.name}" requires human approval (risk: ${candidate.risk})`
821
+ };
822
+ }
823
+ };
824
+ /** 规则组合策略:首个命中的规则胜出,全部未命中走 fallback(默认 AlwaysHuman) */
825
+ var CompositeApprovalPolicy = class {
826
+ #rules;
827
+ #fallback;
828
+ constructor(rules, fallback) {
829
+ this.#rules = rules;
830
+ this.#fallback = fallback ?? new AlwaysHumanApprovalPolicy();
831
+ }
832
+ evaluate(candidate) {
833
+ for (const rule of this.#rules) {
834
+ const decision = rule(candidate);
835
+ if (decision) return decision;
836
+ }
837
+ return this.#fallback.evaluate(candidate);
838
+ }
839
+ };
840
+ /** 审批工作流:review(UiBridge confirm 真实接线)/ publish(校验→安装→版本→审计) */
841
+ var ApprovalWorkflow = class {
842
+ #policy;
843
+ #audit;
844
+ #store;
845
+ #skillManager;
846
+ #versions;
847
+ #fs;
848
+ constructor(deps) {
849
+ this.#policy = deps.policy;
850
+ this.#audit = deps.audit;
851
+ this.#store = deps.store;
852
+ this.#skillManager = deps.skillManager;
853
+ this.#versions = deps.versions;
854
+ this.#fs = deps.fs ?? new NodeFS();
855
+ }
856
+ /** 策略评估;needs-human 时经 UiBridge confirm 真实询问,按应答迁移状态 */
857
+ async review(candidateId, input) {
858
+ const candidate = await this.#store.get(candidateId);
859
+ if (candidate.status !== "draft" && candidate.status !== "pending-review") throw new WebSkillError("GOVERNANCE_FAILED", `Candidate "${candidateId}" cannot be reviewed from status "${candidate.status}"`);
860
+ const decision = this.#policy.evaluate(candidate);
861
+ let approved;
862
+ if (decision.needsHuman) {
863
+ if (!input.uiBridge) {
864
+ await this.#store.updateStatus(candidateId, "pending-review");
865
+ throw new WebSkillError("APPROVAL_REQUIRED", `Candidate "${candidate.name}" requires human approval: ${decision.reason}`);
866
+ }
867
+ await this.#store.updateStatus(candidateId, "pending-review");
868
+ const response = await input.uiBridge.request({
869
+ type: "confirm",
870
+ id: `approval-${candidateId}`,
871
+ message: `Approve candidate "${candidate.name}" (risk: ${candidate.risk})? ${decision.reason}`,
872
+ defaultValue: false
873
+ });
874
+ approved = response.cancelled !== true && response.value === true;
875
+ } else approved = true;
876
+ const updated = await this.#store.updateStatus(candidateId, approved ? "approved" : "rejected");
877
+ await this.#audit.append({
878
+ type: "candidate.reviewed",
879
+ target: candidateId,
880
+ actor: input.actor,
881
+ data: {
882
+ approved,
883
+ reason: decision.reason
884
+ }
885
+ });
886
+ return updated;
887
+ }
888
+ /** publish 全链路:approved 前置 → 写出 staging → validateSkills → install → 版本 → 审计 */
889
+ async publish(candidateId, input) {
890
+ const candidate = await this.#store.get(candidateId);
891
+ if (candidate.status !== "approved") throw new WebSkillError("APPROVAL_REQUIRED", `Candidate "${candidate.name}" must be approved before publishing (status: ${candidate.status})`);
892
+ const stagingRoot = (await mkdtemp(path.join(tmpdir(), "webskill-candidate-"))).split(path.sep).join("/");
893
+ try {
894
+ const skillDir = `${stagingRoot}/${candidate.name}`;
895
+ for (const file of candidate.files) await this.#fs.writeText(resolveInsideRoot(skillDir, file.path), file.content);
896
+ const report = await validateSkills(this.#fs, [stagingRoot]);
897
+ if (!report.ok) {
898
+ const errors = report.issues.filter((i) => i.severity === "error");
899
+ throw new WebSkillError("GOVERNANCE_FAILED", `Candidate "${candidate.name}" failed validation: ${errors.map((i) => i.message).join("; ")}`, errors);
900
+ }
901
+ const manifest = await this.#skillManager.install({
902
+ type: "local",
903
+ path: skillDir
904
+ });
905
+ const archiveOut = `${stagingRoot}/version-archive.zip`;
906
+ await exportArchive(this.#fs, `${this.#skillManager.managedRoot}/${candidate.name}`, {
907
+ format: "zip",
908
+ outPath: archiveOut
909
+ });
910
+ await this.#versions.add(candidate.name, {
911
+ reason: `Publish candidate ${candidateId}`,
912
+ manifest,
913
+ archive: await this.#fs.readBinary(archiveOut)
914
+ });
915
+ await this.#store.updateStatus(candidateId, "published");
916
+ await this.#audit.append({
917
+ type: "skill.published",
918
+ target: candidate.name,
919
+ actor: input.actor,
920
+ data: {
921
+ candidateId,
922
+ digest: manifest.integrity.digest
923
+ }
924
+ });
925
+ return manifest;
926
+ } catch (e) {
927
+ if (e instanceof WebSkillError) throw e;
928
+ throw new WebSkillError("GOVERNANCE_FAILED", `Failed to publish candidate "${candidateId}": ${messageOf(e)}`, e);
929
+ } finally {
930
+ try {
931
+ await this.#fs.remove(stagingRoot, { recursive: true });
932
+ } catch {}
933
+ }
934
+ }
935
+ /**
936
+ * 真实回滚(受审批保护:仅经显式 actor 调用并全程审计):
937
+ * 版本归档解包 → staging 校验 → 原子安装(复用安装管线 swap)→ 追加新版本 + 审计。
938
+ * RepairPlanner 的 rollback 选项(targetVersionId)经本方法执行。
939
+ */
940
+ async applyRollback(skillName, versionId, input) {
941
+ assertSafePathSegment(skillName, "skill name");
942
+ assertSafePathSegment(versionId, "version id");
943
+ const version = await this.#versions.get(skillName, versionId);
944
+ const archive = await this.#versions.readArchive(skillName, versionId);
945
+ const stagingRoot = (await mkdtemp(path.join(tmpdir(), "webskill-rollback-"))).split(path.sep).join("/");
946
+ try {
947
+ const skillDir = `${stagingRoot}/${skillName}`;
948
+ for (const [rel, content] of await unzipWithLimits(archive)) {
949
+ if (rel.endsWith("/")) continue;
950
+ await this.#fs.writeBinary(resolveInsideRoot(skillDir, rel), content);
951
+ }
952
+ const report = await validateSkills(this.#fs, [stagingRoot]);
953
+ if (!report.ok) {
954
+ const errors = report.issues.filter((i) => i.severity === "error");
955
+ throw new WebSkillError("GOVERNANCE_FAILED", `Rollback archive of "${skillName}" failed validation: ${errors.map((i) => i.message).join("; ")}`, errors);
956
+ }
957
+ const manifest = await this.#skillManager.install({
958
+ type: "local",
959
+ path: skillDir
960
+ });
961
+ if (manifest.integrity.digest !== version.manifest.integrity.digest) throw new WebSkillError("GOVERNANCE_FAILED", `Rollback of "${skillName}" to version "${versionId}" produced a digest mismatch: expected ${version.manifest.integrity.digest}, got ${manifest.integrity.digest}`);
962
+ await this.#versions.add(skillName, {
963
+ reason: input.reason ?? `Rollback to version ${versionId}`,
964
+ manifest,
965
+ archive
966
+ });
967
+ await this.#audit.append({
968
+ type: "skill.rolled_back",
969
+ target: skillName,
970
+ actor: input.actor,
971
+ data: {
972
+ targetVersionId: versionId,
973
+ reason: input.reason
974
+ }
975
+ });
976
+ return manifest;
977
+ } catch (e) {
978
+ if (e instanceof WebSkillError) throw e;
979
+ throw new WebSkillError("GOVERNANCE_FAILED", `Failed to roll back "${skillName}" to version "${versionId}": ${messageOf(e)}`, e);
980
+ } finally {
981
+ try {
982
+ await this.#fs.remove(stagingRoot, { recursive: true });
983
+ } catch {}
984
+ }
985
+ }
986
+ };
987
+ /**
988
+ * 治理评估专用 runtime 装配(不可信技能试用路径):
989
+ * 默认注入 ProcessSandboxExecutor(fork + --permission 真实进程隔离;子进程
990
+ * env 默认清空防密钥泄露,需透传时经 ProcessSandboxOptions.envWhitelist 显式放行)。
991
+ * 可配置 executor 切回 SandboxedScriptExecutor(worker_threads 能力面收敛形态,
992
+ * 非安全边界;envWhitelist 同样适用于该执行器)。
993
+ */
994
+ function createEvaluationRuntime(deps) {
995
+ return new WebSkillRuntime({
996
+ ...deps,
997
+ executor: deps.executor ?? new ProcessSandboxExecutor(deps.fs)
998
+ });
999
+ }
989
1000
  const EXTRACT_PROMPT = (doc, nameHint) => [
990
1001
  "Extract an executable skill from the following document as STRICT JSON only.",
991
1002
  "Schema: {\"name\": string, \"description\": string, \"risk\": \"low\"|\"medium\"|\"high\",",