@webskill/sdk 0.1.4 → 0.1.5
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/browser.d.ts +8 -4
- package/dist/browser.js +18 -20
- package/dist/{dist-MTc2KB03.js → dist-CfBOjJ4p.js} +1 -1
- package/dist/{dist-DAn07zHu.js → dist-DuGN5x0v.js} +100 -15
- package/dist/{dist-BVXZF7H_.js → dist-LIFS3ZjI.js} +95 -51
- package/dist/{dist-DvoBsChX.js → dist-fZFZaf43.js} +133 -7
- package/dist/governance.d.ts +9 -3
- package/dist/governance.js +18 -3
- package/dist/{index-npFMt2Zw.d.ts → index-Bun1aEf4.d.ts} +6 -2
- package/dist/{index-BSS3EWY-.d.ts → index-COuOu6gw.d.ts} +19 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +9 -3
- package/dist/node.d.ts +3 -3
- package/dist/node.js +3 -3
- package/dist/sandboxWorkerEntry.js +20 -3
- package/dist/{testing-S8SN9Lc6.js → testing-BmR48Pn1.js} +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{types-CgNC-oQu-DYrxhD3z.d.ts → types-CgNC-oQu-DEcIBJKG.d.ts} +29 -3
- package/dist/ui-react.d.ts +1 -1
- package/dist/ui-react.js +1 -1
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui-vue.js +1 -1
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +2 -2
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { C as FileStat, I as SkillInstallSource, K as VerifyResult, W as SkillsLockfile, _ as RenderResultRequest, a as InteractionPolicy, c as LlmClient, d as LlmResponse, f as LlmStreamEvent, o as InteractionRequest, s as InteractionResponse, v as UiBridge, w as FileSystemProvider, y as ArchiveLimits, z as SkillManifest } from "./types-CgNC-oQu-DEcIBJKG.js";
|
|
2
|
+
import { N as NetworkPolicy, St as parseBridgeRequest, X as ScriptExecutionContext, Z as ScriptExecutor, ct as WebSkillApi, d as BridgeCapabilities, dt as bridgeError, et as ToolDefinition, m as BridgeResponse, nt as ToolResult, p as BridgeRequest, u as ApprovalScope, v as ExternalSkillProvider, y as ExternalToolSource } from "./index-COuOu6gw.js";
|
|
3
3
|
//#region ../browser/dist/index.d.ts
|
|
4
4
|
//#region src/fs/featureDetection.d.ts
|
|
5
5
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
|
@@ -83,6 +83,10 @@ declare class BrowserSkillManager {
|
|
|
83
83
|
fs: FileSystemProvider;
|
|
84
84
|
managedRoot: string;
|
|
85
85
|
fetchImpl?: typeof fetch;
|
|
86
|
+
/** 归档体积三重上限(缺省 DEFAULT_ARCHIVE_LIMITS) */
|
|
87
|
+
archiveLimits?: ArchiveLimits;
|
|
88
|
+
/** install/uninstall 成功后的变更回调(宿主接线缓存失效,如 WebSkillRuntime.invalidate) */
|
|
89
|
+
onChanged?: () => void;
|
|
86
90
|
});
|
|
87
91
|
install(source: SkillInstallSource, options?: {
|
|
88
92
|
expectedSha256?: string;
|
|
@@ -99,10 +103,10 @@ declare class BrowserSkillManager {
|
|
|
99
103
|
//#endregion
|
|
100
104
|
//#region src/skillManagement/zipExtract.d.ts
|
|
101
105
|
/**
|
|
102
|
-
* zip 解包(fflate
|
|
106
|
+
* zip 解包(fflate 流式,单条目/总解压体积上限,浏览器无依赖)。
|
|
103
107
|
* 每个条目路径过 resolveInsideRoot:拒绝 `..` 段、绝对路径、反斜杠穿越(与 node 规则一致)。
|
|
104
108
|
*/
|
|
105
|
-
declare function extractZipWeb(fs: FileSystemProvider, data: Uint8Array, destRoot: string): Promise<void>;
|
|
109
|
+
declare function extractZipWeb(fs: FileSystemProvider, data: Uint8Array, destRoot: string, limits?: ArchiveLimits): Promise<void>;
|
|
106
110
|
//#endregion
|
|
107
111
|
//#region src/skillManagement/checksumWeb.d.ts
|
|
108
112
|
/** WebCrypto sha256(浏览器/Worker 通用;Node 20+ 全局 crypto 同样可用) */
|
package/dist/browser.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as mergeCatalogEntries, M as normalizeToolContent, N as parseBridgeRequest, S as bridgeError, T as createWebSkillApi, c as FsArtifactStore, h as ProgressiveRouter, i as AgentLoop, j as networkUrlHost, k as isNetworkAllowed, l as FsMemoryStore, m as OpenAiCompatibleClient, o as CapabilityApproval, u as FsRunSnapshotStore, y as RUN_SNAPSHOT_SCHEMA_VERSION } from "./dist-
|
|
3
|
-
import { n as MockLlmClient } from "./testing-
|
|
4
|
-
import { unzipSync } from "fflate";
|
|
1
|
+
import { A as validateSkills, C as parseSkillPackManifest, O as resolveInsideRoot, S as parseSkillMarkdown, c as SkillDiscovery, f as buildCatalog, i as SKILL_MANIFEST_FILE, j as verifyManifest, k as unzipWithLimits, p as buildManifest, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, u as WebSkillError, v as exportSkills, w as readResponseWithLimit, y as isValidSkillName } from "./dist-fZFZaf43.js";
|
|
2
|
+
import { A as mergeCatalogEntries, M as normalizeToolContent, N as parseBridgeRequest, S as bridgeError, T as createWebSkillApi, c as FsArtifactStore, h as ProgressiveRouter, i as AgentLoop, j as networkUrlHost, k as isNetworkAllowed, l as FsMemoryStore, m as OpenAiCompatibleClient, o as CapabilityApproval, u as FsRunSnapshotStore, y as RUN_SNAPSHOT_SCHEMA_VERSION } from "./dist-DuGN5x0v.js";
|
|
3
|
+
import { n as MockLlmClient } from "./testing-BmR48Pn1.js";
|
|
5
4
|
|
|
6
5
|
//#region ../browser/dist/index.js
|
|
7
6
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
|
@@ -421,17 +420,11 @@ async function removeDirQuiet(fs, dir) {
|
|
|
421
420
|
} catch {}
|
|
422
421
|
}
|
|
423
422
|
/**
|
|
424
|
-
* zip 解包(fflate
|
|
423
|
+
* zip 解包(fflate 流式,单条目/总解压体积上限,浏览器无依赖)。
|
|
425
424
|
* 每个条目路径过 resolveInsideRoot:拒绝 `..` 段、绝对路径、反斜杠穿越(与 node 规则一致)。
|
|
426
425
|
*/
|
|
427
|
-
async function extractZipWeb(fs, data, destRoot) {
|
|
428
|
-
|
|
429
|
-
try {
|
|
430
|
-
entries = unzipSync(data);
|
|
431
|
-
} catch (e) {
|
|
432
|
-
throw new WebSkillError("INSTALL_FAILED", `Failed to read zip archive: ${e instanceof Error ? e.message : String(e)}`, e);
|
|
433
|
-
}
|
|
434
|
-
for (const [entryPath, content] of Object.entries(entries)) {
|
|
426
|
+
async function extractZipWeb(fs, data, destRoot, limits) {
|
|
427
|
+
for (const [entryPath, content] of await unzipWithLimits(data, limits)) {
|
|
435
428
|
const target = resolveInsideRoot(destRoot, entryPath);
|
|
436
429
|
if (entryPath.endsWith("/")) await fs.mkdir(target);
|
|
437
430
|
else await fs.writeBinary(target, content);
|
|
@@ -450,10 +443,14 @@ var BrowserSkillManager = class {
|
|
|
450
443
|
#fs;
|
|
451
444
|
#managedRoot;
|
|
452
445
|
#fetchImpl;
|
|
446
|
+
#archiveLimits;
|
|
447
|
+
#onChanged;
|
|
453
448
|
constructor(deps) {
|
|
454
449
|
this.#fs = deps.fs;
|
|
455
450
|
this.#managedRoot = deps.managedRoot.replace(/\/+$/, "");
|
|
456
451
|
this.#fetchImpl = deps.fetchImpl;
|
|
452
|
+
this.#archiveLimits = deps.archiveLimits;
|
|
453
|
+
this.#onChanged = deps.onChanged;
|
|
457
454
|
}
|
|
458
455
|
async install(source, options) {
|
|
459
456
|
const fs = this.#fs;
|
|
@@ -470,7 +467,7 @@ var BrowserSkillManager = class {
|
|
|
470
467
|
throw new WebSkillError("INSTALL_FAILED", `Download failed: ${messageOf$1(e)}`, e);
|
|
471
468
|
}
|
|
472
469
|
if (!res.ok) throw new WebSkillError("INSTALL_FAILED", `Download failed with HTTP ${res.status}`);
|
|
473
|
-
data =
|
|
470
|
+
data = await readResponseWithLimit(res, this.#archiveLimits);
|
|
474
471
|
} else data = new Uint8Array(source.data);
|
|
475
472
|
if (options?.expectedSha256 !== void 0) {
|
|
476
473
|
const actual = await sha256HexWeb(data);
|
|
@@ -478,7 +475,7 @@ var BrowserSkillManager = class {
|
|
|
478
475
|
}
|
|
479
476
|
const contentDir = `${stagingRoot}/content`;
|
|
480
477
|
await fs.mkdir(contentDir);
|
|
481
|
-
await extractZipWeb(fs, data, contentDir);
|
|
478
|
+
await extractZipWeb(fs, data, contentDir, this.#archiveLimits);
|
|
482
479
|
const packFilePath = `${contentDir}/${SKILL_PACK_FILE}`;
|
|
483
480
|
if (await fs.exists(packFilePath)) return (await this.#installPack({
|
|
484
481
|
contentDir,
|
|
@@ -534,6 +531,7 @@ var BrowserSkillManager = class {
|
|
|
534
531
|
throw e;
|
|
535
532
|
}
|
|
536
533
|
targetDir = void 0;
|
|
534
|
+
this.#onChanged?.();
|
|
537
535
|
return manifest;
|
|
538
536
|
} catch (e) {
|
|
539
537
|
if (targetDir) await removeDirQuiet(fs, targetDir);
|
|
@@ -611,6 +609,7 @@ var BrowserSkillManager = class {
|
|
|
611
609
|
installed.length = 0;
|
|
612
610
|
throw e;
|
|
613
611
|
}
|
|
612
|
+
this.#onChanged?.();
|
|
614
613
|
return manifests;
|
|
615
614
|
} catch (e) {
|
|
616
615
|
for (const targetDir of installed) await removeDirQuiet(fs, targetDir);
|
|
@@ -641,6 +640,7 @@ var BrowserSkillManager = class {
|
|
|
641
640
|
try {
|
|
642
641
|
await this.#fs.remove(targetDir, { recursive: true });
|
|
643
642
|
await this.#removeLockEntry(name);
|
|
643
|
+
this.#onChanged?.();
|
|
644
644
|
} catch (e) {
|
|
645
645
|
throw new WebSkillError("UNINSTALL_FAILED", `Failed to uninstall "${name}": ${messageOf$1(e)}`, e);
|
|
646
646
|
}
|
|
@@ -649,11 +649,9 @@ var BrowserSkillManager = class {
|
|
|
649
649
|
if (!isValidSkillName(name)) throw new WebSkillError("INTEGRITY_FAILED", `Invalid skill name (path traversal rejected): ${JSON.stringify(name)}`);
|
|
650
650
|
const manifest = await this.#readManifest(`${this.#managedRoot}/${name}`);
|
|
651
651
|
const actualHashes = /* @__PURE__ */ new Map();
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
}
|
|
656
|
-
return verifyManifest(manifest, actualHashes);
|
|
652
|
+
const actualFiles = await listFiles(this.#fs, `${this.#managedRoot}/${name}`);
|
|
653
|
+
for (const rel of actualFiles) actualHashes.set(rel, await sha256HexWeb(await this.#fs.readBinary(`${this.#managedRoot}/${name}/${rel}`)));
|
|
654
|
+
return verifyManifest(manifest, actualHashes, actualFiles);
|
|
657
655
|
}
|
|
658
656
|
async listInstalled() {
|
|
659
657
|
const path = lockfilePath(this.#managedRoot);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as validateSkills, O as resolveInsideRoot, S as parseSkillMarkdown, T as renderAvailableSkillsXml, c as SkillDiscovery, d as assertSafePathSegment, f as buildCatalog, l as SkillReader, u as WebSkillError } from "./dist-fZFZaf43.js";
|
|
2
2
|
import { t as MemoryArtifactStore } from "./memoryArtifactStore-C9lFVqPF-yFz6yJj0.js";
|
|
3
3
|
|
|
4
4
|
//#region ../runtime/dist/index.js
|
|
@@ -1050,6 +1050,7 @@ var AgentLoop = class {
|
|
|
1050
1050
|
maxTurns: config.maxTurns ?? 10,
|
|
1051
1051
|
totalTimeoutMs: config.totalTimeoutMs ?? 12e4,
|
|
1052
1052
|
toolTimeoutMs: config.toolTimeoutMs ?? 3e4,
|
|
1053
|
+
toolResultMaxBytes: config.toolResultMaxBytes ?? 1e5,
|
|
1053
1054
|
temperature: config.temperature,
|
|
1054
1055
|
renderResult: config.renderResult
|
|
1055
1056
|
};
|
|
@@ -1089,7 +1090,12 @@ var AgentLoop = class {
|
|
|
1089
1090
|
interactionSeq: 0,
|
|
1090
1091
|
messages: [],
|
|
1091
1092
|
turn: 0,
|
|
1092
|
-
renderBlocks: []
|
|
1093
|
+
renderBlocks: [],
|
|
1094
|
+
startMs,
|
|
1095
|
+
pausedMs: 0,
|
|
1096
|
+
maxTurns: this.#config.maxTurns,
|
|
1097
|
+
totalTimeoutMs: this.#config.totalTimeoutMs,
|
|
1098
|
+
controller: new AbortController()
|
|
1093
1099
|
};
|
|
1094
1100
|
if (this.#deps.hooks && !this.#deps.hooks.onWarning) this.#deps.hooks.onWarning = (message) => trace.record("run.warning", { message });
|
|
1095
1101
|
const route = this.#deps.catalogFilter ? {
|
|
@@ -1117,21 +1123,42 @@ var AgentLoop = class {
|
|
|
1117
1123
|
content: input.userPrompt
|
|
1118
1124
|
}];
|
|
1119
1125
|
try {
|
|
1120
|
-
return await this.#turnLoop(state,
|
|
1126
|
+
return await this.#turnLoop(state, 1, externalSpecs);
|
|
1121
1127
|
} catch (e) {
|
|
1122
1128
|
if (e instanceof WebSkillError && e.code === "RUN_FAILED") return this.#finish(state, "failed", "hook-error", messageOf$1(e), "RUN_FAILED");
|
|
1123
1129
|
throw e;
|
|
1124
1130
|
}
|
|
1125
1131
|
}
|
|
1132
|
+
/** 总时长消耗(wall - 交互等待);交互期间不计入 totalTimeout */
|
|
1133
|
+
#elapsed(state) {
|
|
1134
|
+
return Date.parse(state.now()) - state.startMs - state.pausedMs;
|
|
1135
|
+
}
|
|
1136
|
+
/** 重设硬期限定时器(剩余 = totalTimeoutMs - 已消耗;耗尽立即 abort) */
|
|
1137
|
+
#armDeadline(state) {
|
|
1138
|
+
if (state.timer) clearTimeout(state.timer);
|
|
1139
|
+
const remaining = state.totalTimeoutMs - this.#elapsed(state);
|
|
1140
|
+
if (remaining <= 0) {
|
|
1141
|
+
state.controller.abort();
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
state.timer = setTimeout(() => state.controller.abort(), remaining);
|
|
1145
|
+
}
|
|
1146
|
+
#disarmDeadline(state) {
|
|
1147
|
+
if (state.timer) {
|
|
1148
|
+
clearTimeout(state.timer);
|
|
1149
|
+
state.timer = void 0;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1126
1152
|
/** 主循环(run 从第 1 轮、resume 从快照轮次续跑;totalTimeout 以 startedAt 续算) */
|
|
1127
|
-
async #turnLoop(state,
|
|
1153
|
+
async #turnLoop(state, startTurn, externalSpecs) {
|
|
1128
1154
|
const finish = (status, reason, output, errorCode) => this.#finish(state, status, reason, output, errorCode);
|
|
1129
1155
|
const { llm } = this.#deps;
|
|
1130
1156
|
const { trace, messages } = state;
|
|
1157
|
+
this.#armDeadline(state);
|
|
1131
1158
|
for (let turn = startTurn;; turn++) {
|
|
1132
1159
|
state.turn = turn;
|
|
1133
|
-
if (turn >
|
|
1134
|
-
if (
|
|
1160
|
+
if (turn > state.maxTurns) return finish("failed", "max-turns", `Agent loop exceeded the maximum of ${state.maxTurns} turns`, "RUN_MAX_TURNS_EXCEEDED");
|
|
1161
|
+
if (this.#elapsed(state) > state.totalTimeoutMs) return finish("failed", "timeout", `Agent loop exceeded the total timeout of ${state.totalTimeoutMs}ms`, "RUN_TIMEOUT");
|
|
1135
1162
|
const toolSpecs = [
|
|
1136
1163
|
toLlmToolSpec(READ_SKILL_FILE_TOOL),
|
|
1137
1164
|
...this.#deps.uiBridge ? [toLlmToolSpec(ASK_USER_TOOL)] : [],
|
|
@@ -1149,14 +1176,17 @@ var AgentLoop = class {
|
|
|
1149
1176
|
model: this.#deps.model,
|
|
1150
1177
|
messages,
|
|
1151
1178
|
tools: toolSpecs,
|
|
1152
|
-
temperature: this.#config.temperature
|
|
1179
|
+
temperature: this.#config.temperature,
|
|
1180
|
+
signal: state.controller.signal
|
|
1153
1181
|
}) : await llm.complete({
|
|
1154
1182
|
model: this.#deps.model,
|
|
1155
1183
|
messages,
|
|
1156
1184
|
tools: toolSpecs,
|
|
1157
|
-
temperature: this.#config.temperature
|
|
1185
|
+
temperature: this.#config.temperature,
|
|
1186
|
+
signal: state.controller.signal
|
|
1158
1187
|
});
|
|
1159
1188
|
} catch (e) {
|
|
1189
|
+
if (state.controller.signal.aborted) return finish("failed", "timeout", `Agent loop exceeded the total timeout of ${state.totalTimeoutMs}ms`, "RUN_TIMEOUT");
|
|
1160
1190
|
const code = e instanceof WebSkillError && (e.code === "LLM_UNAVAILABLE" || e.code === "LLM_REQUEST_FAILED") ? e.code : "LLM_REQUEST_FAILED";
|
|
1161
1191
|
return finish("failed", "llm-error", messageOf$1(e), code);
|
|
1162
1192
|
}
|
|
@@ -1183,13 +1213,14 @@ var AgentLoop = class {
|
|
|
1183
1213
|
messages.push({
|
|
1184
1214
|
role: "tool",
|
|
1185
1215
|
toolCallId: call.id,
|
|
1186
|
-
content:
|
|
1216
|
+
content: await this.#serializeToolResult(call, result, state)
|
|
1187
1217
|
});
|
|
1188
1218
|
}
|
|
1189
1219
|
}
|
|
1190
1220
|
}
|
|
1191
1221
|
/** 统一终态处理:completed/failed/cancelled;终态即删快照(快照是续命机制,审计归治理) */
|
|
1192
1222
|
async #finish(state, status, reason, output, errorCode) {
|
|
1223
|
+
this.#disarmDeadline(state);
|
|
1193
1224
|
const { run, trace } = state;
|
|
1194
1225
|
run.status = status;
|
|
1195
1226
|
run.terminationReason = reason;
|
|
@@ -1293,7 +1324,12 @@ var AgentLoop = class {
|
|
|
1293
1324
|
interactionSeq: 0,
|
|
1294
1325
|
messages: snapshot.messages.map((m) => ({ ...m })),
|
|
1295
1326
|
turn: snapshot.turn,
|
|
1296
|
-
renderBlocks: []
|
|
1327
|
+
renderBlocks: [],
|
|
1328
|
+
startMs,
|
|
1329
|
+
pausedMs: 0,
|
|
1330
|
+
maxTurns: snapshot.config.maxTurns,
|
|
1331
|
+
totalTimeoutMs: snapshot.config.totalTimeoutMs,
|
|
1332
|
+
controller: new AbortController()
|
|
1297
1333
|
};
|
|
1298
1334
|
if (this.#deps.hooks && !this.#deps.hooks.onWarning) this.#deps.hooks.onWarning = (message) => trace.record("run.warning", { message });
|
|
1299
1335
|
trace.record("run.resumed", { data: {
|
|
@@ -1364,7 +1400,7 @@ var AgentLoop = class {
|
|
|
1364
1400
|
}
|
|
1365
1401
|
}))).flat();
|
|
1366
1402
|
try {
|
|
1367
|
-
return await this.#turnLoop(state,
|
|
1403
|
+
return await this.#turnLoop(state, snapshot.turn + 1, externalSpecs);
|
|
1368
1404
|
} catch (e) {
|
|
1369
1405
|
if (e instanceof WebSkillError && e.code === "RUN_FAILED") return this.#finish(state, "failed", "hook-error", messageOf$1(e), "RUN_FAILED");
|
|
1370
1406
|
throw e;
|
|
@@ -1431,8 +1467,18 @@ var AgentLoop = class {
|
|
|
1431
1467
|
* 取消 → RunTerminated(cancelled);超时 → RunTerminated(interaction-timeout)。
|
|
1432
1468
|
*/
|
|
1433
1469
|
async #interact(state, request, traceData) {
|
|
1470
|
+
if (!this.#deps.uiBridge) throw new Error("interact() requires a UiBridge");
|
|
1471
|
+
this.#disarmDeadline(state);
|
|
1472
|
+
const waitStartMs = Date.parse(state.now());
|
|
1473
|
+
try {
|
|
1474
|
+
return await this.#interactInner(state, request, traceData);
|
|
1475
|
+
} finally {
|
|
1476
|
+
state.pausedMs += Date.parse(state.now()) - waitStartMs;
|
|
1477
|
+
this.#armDeadline(state);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
async #interactInner(state, request, traceData) {
|
|
1434
1481
|
const bridge = this.#deps.uiBridge;
|
|
1435
|
-
if (!bridge) throw new Error("interact() requires a UiBridge");
|
|
1436
1482
|
const { run } = state;
|
|
1437
1483
|
run.status = "interrupted";
|
|
1438
1484
|
run.interruptExpiresAt = new Date(Date.parse(state.now()) + this.#policy.interactionTimeoutMs).toISOString();
|
|
@@ -1560,6 +1606,7 @@ var AgentLoop = class {
|
|
|
1560
1606
|
if (typeof pathArg !== "string") return toolError("TOOL_EXECUTION_FAILED", "read_skill_file \"path\" must be a string");
|
|
1561
1607
|
if (skillName.startsWith("mcp://")) return this.#handleReadExternalSkill(skillName, pathArg, state);
|
|
1562
1608
|
try {
|
|
1609
|
+
if (await this.#guardDenied("canRead", skillName)) return toolError("SKILL_DISABLED", `Skill "${skillName}" is blocked by the skill state guard (read)`);
|
|
1563
1610
|
const text = await state.reader.readSkillFile(skillName, pathArg);
|
|
1564
1611
|
let note = "";
|
|
1565
1612
|
if (pathArg === "SKILL.md" && !state.activated.has(skillName)) note = await this.#activateSkill(skillName, state, void 0, text);
|
|
@@ -1618,8 +1665,35 @@ var AgentLoop = class {
|
|
|
1618
1665
|
const detail = failures.length > 0 ? ` (provider errors: ${failures.join("; ")})` : "";
|
|
1619
1666
|
return toolError("SKILL_NOT_FOUND", `Skill not found via external providers: ${skillKey}${detail}`);
|
|
1620
1667
|
}
|
|
1668
|
+
/**
|
|
1669
|
+
* 工具结果回喂序列化:超过 toolResultMaxBytes(默认 100KB)时头尾保留截断,
|
|
1670
|
+
* 完整内容经 artifactStore 落 artifact,回喂摘要含 artifact id。
|
|
1671
|
+
*/
|
|
1672
|
+
async #serializeToolResult(call, result, state) {
|
|
1673
|
+
const text = JSON.stringify(result);
|
|
1674
|
+
const max = this.#config.toolResultMaxBytes;
|
|
1675
|
+
if (text.length <= max) return text;
|
|
1676
|
+
const artifact = await this.#deps.artifactStore.createTextArtifact({
|
|
1677
|
+
runId: state.runId,
|
|
1678
|
+
path: `tool-results/${call.id}.json`,
|
|
1679
|
+
content: text
|
|
1680
|
+
});
|
|
1681
|
+
const head = text.slice(0, Math.floor(max * .6));
|
|
1682
|
+
const tail = text.slice(-Math.floor(max * .3));
|
|
1683
|
+
return `${head}\n...[truncated ${text.length - head.length - tail.length} chars; full tool result saved as artifact "${artifact.id}"]...\n${tail}`;
|
|
1684
|
+
}
|
|
1685
|
+
/** SkillStateGuard 判定(无注入默认全放行;仅显式 false 拦截) */
|
|
1686
|
+
async #guardDenied(kind, skillName) {
|
|
1687
|
+
const check = this.#deps.skillStateGuard?.[kind];
|
|
1688
|
+
if (!check) return false;
|
|
1689
|
+
return await check(skillName) === false;
|
|
1690
|
+
}
|
|
1621
1691
|
/** 首次读到 SKILL.md 时激活技能:加载其 scripts 工具定义,供后续轮次使用;dependencies 级联激活(via 记录来源) */
|
|
1622
1692
|
async #activateSkill(skillName, state, via, skillMdText) {
|
|
1693
|
+
if (await this.#guardDenied("canActivate", skillName)) {
|
|
1694
|
+
state.trace.record("run.warning", { message: `Skill "${skillName}" is blocked by the skill state guard (activate)` });
|
|
1695
|
+
return "";
|
|
1696
|
+
}
|
|
1623
1697
|
state.activated.add(skillName);
|
|
1624
1698
|
state.trace.record("skill.activated", { data: {
|
|
1625
1699
|
skillName,
|
|
@@ -1735,12 +1809,14 @@ var AgentLoop = class {
|
|
|
1735
1809
|
onArtifactCreated: (artifact) => createdIds.add(artifact.id)
|
|
1736
1810
|
});
|
|
1737
1811
|
try {
|
|
1812
|
+
if (await this.#guardDenied("canExecute", skillName)) return toolError("SKILL_DISABLED", `Skill "${skillName}" is blocked by the skill state guard (execute)`);
|
|
1813
|
+
const remainingMs = state.totalTimeoutMs - this.#elapsed(state);
|
|
1738
1814
|
const result = await this.#deps.executor.execute({
|
|
1739
1815
|
skillRoot: root,
|
|
1740
1816
|
scriptName,
|
|
1741
1817
|
args,
|
|
1742
1818
|
context,
|
|
1743
|
-
timeoutMs: state.toolTimeoutMs
|
|
1819
|
+
timeoutMs: Math.max(1, Math.min(state.toolTimeoutMs, remainingMs))
|
|
1744
1820
|
});
|
|
1745
1821
|
if (createdIds.size > 0) {
|
|
1746
1822
|
const fresh = (await this.#deps.artifactStore.listArtifacts(state.runId)).filter((a) => createdIds.has(a.id));
|
|
@@ -1919,6 +1995,13 @@ var WebSkillRuntime = class {
|
|
|
1919
1995
|
get events() {
|
|
1920
1996
|
return this.#events;
|
|
1921
1997
|
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Catalog 缓存失效:下次 run/discover 重新扫描技能目录。
|
|
2000
|
+
* 与 SkillManager onChanged 接线:`new SkillManager({ ..., onChanged: () => runtime.invalidate() })`。
|
|
2001
|
+
*/
|
|
2002
|
+
invalidate() {
|
|
2003
|
+
this.#catalogCache = void 0;
|
|
2004
|
+
}
|
|
1922
2005
|
async discover() {
|
|
1923
2006
|
const result = await this.#discovery.discover();
|
|
1924
2007
|
this.#catalogCache = {
|
|
@@ -1960,7 +2043,8 @@ var WebSkillRuntime = class {
|
|
|
1960
2043
|
externalTools: this.#deps.externalTools,
|
|
1961
2044
|
skillProviders: this.#deps.skillProviders,
|
|
1962
2045
|
catalogFilter: this.#deps.catalogFilter,
|
|
1963
|
-
snapshotStore: this.#deps.snapshotStore
|
|
2046
|
+
snapshotStore: this.#deps.snapshotStore,
|
|
2047
|
+
skillStateGuard: this.#deps.skillStateGuard
|
|
1964
2048
|
}, this.#deps.config).run({
|
|
1965
2049
|
sessionId: this.#session.id,
|
|
1966
2050
|
userPrompt,
|
|
@@ -2056,7 +2140,8 @@ var WebSkillRuntime = class {
|
|
|
2056
2140
|
externalTools: this.#deps.externalTools,
|
|
2057
2141
|
skillProviders: this.#deps.skillProviders,
|
|
2058
2142
|
catalogFilter: this.#deps.catalogFilter,
|
|
2059
|
-
snapshotStore: store
|
|
2143
|
+
snapshotStore: store,
|
|
2144
|
+
skillStateGuard: this.#deps.skillStateGuard
|
|
2060
2145
|
}, this.#deps.config).resume(snapshot);
|
|
2061
2146
|
}
|
|
2062
2147
|
};
|