@webskill/sdk 0.2.4 → 0.2.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 +2 -2
- package/dist/browser.js +17 -13
- package/dist/{dist-CV64gN62.js → dist-B77plHjw.js} +147 -94
- package/dist/{dist-D7MsoMPx.js → dist-BQzncxXg.js} +70 -3
- package/dist/{dist-Chgf2tcy.js → dist-Chk8iB-E.js} +76 -31
- package/dist/{dist-CeNAzFYi.js → dist-DNSG9FqC.js} +108 -69
- package/dist/governance.d.ts +7 -4
- package/dist/governance.js +20 -8
- package/dist/{index-DrHelz72.d.ts → index-CsDJvYGV.d.ts} +5 -3
- package/dist/{index-DZShzhon.d.ts → index-DfINBEOy.d.ts} +11 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +6 -2
- package/dist/mcp.js +14 -14
- package/dist/node.d.ts +3 -3
- package/dist/node.js +3 -3
- package/dist/{testing-BN18eqbD.js → testing-BUoXvm1u.js} +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{types-CKm5G_eQ-BqyXnvoR.d.ts → types-CKm5G_eQ-krKWW8WV.d.ts} +32 -2
- package/dist/ui-react.d.ts +3 -1
- package/dist/ui-react.js +8 -1
- package/dist/ui-vue.d.ts +3 -1
- package/dist/ui-vue.js +8 -1
- package/dist/ui.d.ts +20 -6
- package/dist/ui.js +3 -3
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Et as parseBridgeRequest, N as NetworkPolicy, Q as ScriptExecutor, Z as ScriptExecutionContext, d as BridgeCapabilities, it as ToolResult, m as BridgeResponse, nt as ToolDefinition, p as BridgeRequest, pt as bridgeError, u as ApprovalScope, ut as WebSkillApi, v as ExternalSkillProvider, y as ExternalToolSource } from "./index-
|
|
1
|
+
import { B as SkillManifest, C as FileStat, G as SkillsLockfile, L as SkillInstallSource, _ as RenderResultRequest, a as InteractionPolicy, c as LlmClient, d as LlmResponse, f as LlmStreamEvent, o as InteractionRequest, q as VerifyResult, s as InteractionResponse, v as UiBridge, w as FileSystemProvider, y as ArchiveLimits } from "./types-CKm5G_eQ-krKWW8WV.js";
|
|
2
|
+
import { Et as parseBridgeRequest, N as NetworkPolicy, Q as ScriptExecutor, Z as ScriptExecutionContext, d as BridgeCapabilities, it as ToolResult, m as BridgeResponse, nt as ToolDefinition, p as BridgeRequest, pt as bridgeError, u as ApprovalScope, ut as WebSkillApi, v as ExternalSkillProvider, y as ExternalToolSource } from "./index-DfINBEOy.js";
|
|
3
3
|
//#region ../browser/dist/index.d.ts
|
|
4
4
|
//#region src/fs/featureDetection.d.ts
|
|
5
5
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
package/dist/browser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as isNetworkAllowed, C as bridgeError, E as createWebSkillApi, F as normalizeToolError, I as parseBridgeRequest, M as networkUrlHost, P as normalizeToolContent, c as FsArtifactStore, h as ProgressiveRouter, i as AgentLoop, j as mergeCatalogEntries, 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-
|
|
1
|
+
import { C as messageOf, D as readResponseWithLimit, E as parseSkillPackManifest, M as unzipWithLimits, N as validateSkills, P as verifyManifest, T as parseSkillMarkdown, b as exportSkills, c as SkillDiscovery, d as assertRemoteUrlAllowed, h as buildManifest, i as SKILL_MANIFEST_FILE, j as resolveInsideRoot, m as buildCatalog, p as atomicWriteText, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, u as WebSkillError, x as isValidSkillName } from "./dist-BQzncxXg.js";
|
|
2
|
+
import { A as isNetworkAllowed, C as bridgeError, E as createWebSkillApi, F as normalizeToolError, I as parseBridgeRequest, M as networkUrlHost, P as normalizeToolContent, c as FsArtifactStore, h as ProgressiveRouter, i as AgentLoop, j as mergeCatalogEntries, l as FsMemoryStore, m as OpenAiCompatibleClient, o as CapabilityApproval, u as FsRunSnapshotStore, y as RUN_SNAPSHOT_SCHEMA_VERSION } from "./dist-B77plHjw.js";
|
|
3
|
+
import { n as MockLlmClient } from "./testing-BUoXvm1u.js";
|
|
4
4
|
|
|
5
5
|
//#region ../browser/dist/index.js
|
|
6
6
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
|
@@ -462,8 +462,7 @@ async function extractZipWeb(fs, data, destRoot, limits) {
|
|
|
462
462
|
else await fs.writeBinary(target, content);
|
|
463
463
|
}
|
|
464
464
|
}
|
|
465
|
-
const
|
|
466
|
-
const asInstallFailed = (e) => e instanceof WebSkillError && (e.code === "INSTALL_FAILED" || e.code === "TOOL_UNSUPPORTED") ? e : new WebSkillError("INSTALL_FAILED", `Install failed: ${messageOf$1(e)}`, e);
|
|
465
|
+
const asInstallFailed = (e) => e instanceof WebSkillError && (e.code === "INSTALL_FAILED" || e.code === "TOOL_UNSUPPORTED") ? e : new WebSkillError("INSTALL_FAILED", `Install failed: ${messageOf(e)}`, e);
|
|
467
466
|
const lockfilePath = (root) => `${root}/${SKILLS_LOCKFILE}`;
|
|
468
467
|
/**
|
|
469
468
|
* D5:浏览器技能安装(http(s) zip / ArrayBuffer;tar/git/npm → TOOL_UNSUPPORTED)。
|
|
@@ -494,9 +493,15 @@ var BrowserSkillManager = class {
|
|
|
494
493
|
if (source.type === "http") {
|
|
495
494
|
let res;
|
|
496
495
|
try {
|
|
497
|
-
|
|
496
|
+
const absolute = new URL(source.url, typeof location !== "undefined" ? location.href : void 0).href;
|
|
497
|
+
const url = assertRemoteUrlAllowed(absolute, {
|
|
498
|
+
allowHttp: source.allowHttp ?? false,
|
|
499
|
+
allowPrivateHosts: source.allowPrivateHosts ?? false
|
|
500
|
+
});
|
|
501
|
+
res = await (this.#fetchImpl ?? fetch)(url.href);
|
|
498
502
|
} catch (e) {
|
|
499
|
-
|
|
503
|
+
if (e instanceof WebSkillError) throw e;
|
|
504
|
+
throw new WebSkillError("INSTALL_FAILED", `Download failed: ${messageOf(e)}`, e);
|
|
500
505
|
}
|
|
501
506
|
if (!res.ok) throw new WebSkillError("INSTALL_FAILED", `Download failed with HTTP ${res.status}`);
|
|
502
507
|
data = await readResponseWithLimit(res, this.#archiveLimits);
|
|
@@ -526,7 +531,7 @@ var BrowserSkillManager = class {
|
|
|
526
531
|
name = metadata.name;
|
|
527
532
|
version = typeof metadata["version"] === "string" ? metadata["version"] : void 0;
|
|
528
533
|
} catch (e) {
|
|
529
|
-
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md: ${messageOf
|
|
534
|
+
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md: ${messageOf(e)}`, e);
|
|
530
535
|
}
|
|
531
536
|
if (!isValidSkillName(name)) throw new WebSkillError("INSTALL_FAILED", `Invalid skill name in SKILL.md (path traversal rejected): ${JSON.stringify(name)}`);
|
|
532
537
|
const finalRoot = `${stagingRoot}/final`;
|
|
@@ -592,7 +597,7 @@ var BrowserSkillManager = class {
|
|
|
592
597
|
name = metadata.name;
|
|
593
598
|
versions.set(name, typeof metadata["version"] === "string" ? metadata["version"] : void 0);
|
|
594
599
|
} catch (e) {
|
|
595
|
-
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md of pack entry "${entry.name}": ${messageOf
|
|
600
|
+
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md of pack entry "${entry.name}": ${messageOf(e)}`, e);
|
|
596
601
|
}
|
|
597
602
|
if (name !== entry.name) throw new WebSkillError("INSTALL_FAILED", `Skill pack entry "${entry.name}" does not match the SKILL.md name "${name}"`);
|
|
598
603
|
await copyDir(fs, skillDir, `${finalRoot}/${name}`);
|
|
@@ -674,7 +679,7 @@ var BrowserSkillManager = class {
|
|
|
674
679
|
await this.#removeLockEntry(name);
|
|
675
680
|
this.#onChanged?.();
|
|
676
681
|
} catch (e) {
|
|
677
|
-
throw new WebSkillError("UNINSTALL_FAILED", `Failed to uninstall "${name}": ${messageOf
|
|
682
|
+
throw new WebSkillError("UNINSTALL_FAILED", `Failed to uninstall "${name}": ${messageOf(e)}`, e);
|
|
678
683
|
}
|
|
679
684
|
}
|
|
680
685
|
async verifyIntegrity(name) {
|
|
@@ -694,7 +699,7 @@ var BrowserSkillManager = class {
|
|
|
694
699
|
try {
|
|
695
700
|
return JSON.parse(await this.#fs.readText(path));
|
|
696
701
|
} catch (e) {
|
|
697
|
-
throw new WebSkillError("INTEGRITY_FAILED", `Skills lockfile at ${path} is corrupted: ${messageOf
|
|
702
|
+
throw new WebSkillError("INTEGRITY_FAILED", `Skills lockfile at ${path} is corrupted: ${messageOf(e)}`, e);
|
|
698
703
|
}
|
|
699
704
|
}
|
|
700
705
|
async #createManifest(skillRoot, input) {
|
|
@@ -726,7 +731,7 @@ var BrowserSkillManager = class {
|
|
|
726
731
|
try {
|
|
727
732
|
return JSON.parse(await this.#fs.readText(path));
|
|
728
733
|
} catch (e) {
|
|
729
|
-
throw new WebSkillError("INTEGRITY_FAILED", `Skill manifest at ${path} is corrupted: ${messageOf
|
|
734
|
+
throw new WebSkillError("INTEGRITY_FAILED", `Skill manifest at ${path} is corrupted: ${messageOf(e)}`, e);
|
|
730
735
|
}
|
|
731
736
|
}
|
|
732
737
|
async #upsertLockEntry(name, entry) {
|
|
@@ -864,7 +869,6 @@ const resolveWorkerFactory = (mode) => {
|
|
|
864
869
|
return blobWorkerFactory;
|
|
865
870
|
};
|
|
866
871
|
const baseName = (p) => p.split("/").pop() ?? p;
|
|
867
|
-
const messageOf = (e) => e instanceof Error ? e.message : String(e);
|
|
868
872
|
/**
|
|
869
873
|
* Web Worker 脚本沙箱执行器:loadDefinition 与 execute 都在 Worker 内完成,
|
|
870
874
|
* 主线程从不 import 技能脚本;每次执行独立 Worker;超时 terminate 强杀。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as messageOf, N as validateSkills, O as renderAvailableSkillsXml, T as parseSkillMarkdown, c as SkillDiscovery, f as assertSafePathSegment, j as resolveInsideRoot, l as SkillReader, m as buildCatalog, u as WebSkillError } from "./dist-BQzncxXg.js";
|
|
2
2
|
import { t as MemoryArtifactStore } from "./memoryArtifactStore-C9lFVqPF-yFz6yJj0.js";
|
|
3
3
|
|
|
4
4
|
//#region ../runtime/dist/index.js
|
|
@@ -967,6 +967,13 @@ function mapFieldType(prop) {
|
|
|
967
967
|
/** 生命周期事件总线:只读观测,支持按阶段或通配订阅 */
|
|
968
968
|
var EventBus = class {
|
|
969
969
|
#listeners = /* @__PURE__ */ new Map();
|
|
970
|
+
/** 监听器异常出口(默认 console.warn;单个监听器抛错不影响其他监听器与 emit 调用方) */
|
|
971
|
+
#onListenerError;
|
|
972
|
+
constructor(onListenerError) {
|
|
973
|
+
this.#onListenerError = onListenerError ?? ((error, event) => {
|
|
974
|
+
console.warn(`EventBus listener threw on phase "${event.phase}": ${error instanceof Error ? error.message : String(error)}`);
|
|
975
|
+
});
|
|
976
|
+
}
|
|
970
977
|
/** 返回取消订阅函数 */
|
|
971
978
|
on(phase, listener) {
|
|
972
979
|
const key = phase;
|
|
@@ -986,8 +993,15 @@ var EventBus = class {
|
|
|
986
993
|
}
|
|
987
994
|
emit(event) {
|
|
988
995
|
const frozen = Object.freeze(event);
|
|
989
|
-
for (const listener of this.#listeners.get(event.phase) ?? []) listener
|
|
990
|
-
for (const listener of this.#listeners.get("*") ?? []) listener
|
|
996
|
+
for (const listener of this.#listeners.get(event.phase) ?? []) this.#dispatch(listener, frozen);
|
|
997
|
+
for (const listener of this.#listeners.get("*") ?? []) this.#dispatch(listener, frozen);
|
|
998
|
+
}
|
|
999
|
+
#dispatch(listener, event) {
|
|
1000
|
+
try {
|
|
1001
|
+
listener(event);
|
|
1002
|
+
} catch (e) {
|
|
1003
|
+
this.#onListenerError(e, event);
|
|
1004
|
+
}
|
|
991
1005
|
}
|
|
992
1006
|
};
|
|
993
1007
|
/**
|
|
@@ -1000,9 +1014,17 @@ var SerializingMemoryStore = class {
|
|
|
1000
1014
|
constructor(inner) {
|
|
1001
1015
|
this.#inner = inner;
|
|
1002
1016
|
}
|
|
1017
|
+
/** 当前链上的 scope 数(监控/测试用;空闲时应回落到 0) */
|
|
1018
|
+
get trackedScopeCount() {
|
|
1019
|
+
return this.#chains.size;
|
|
1020
|
+
}
|
|
1003
1021
|
#serialize(scope, fn) {
|
|
1004
1022
|
const next = (this.#chains.get(scope) ?? Promise.resolve()).then(fn, fn);
|
|
1005
|
-
|
|
1023
|
+
const guarded = next.catch(() => void 0);
|
|
1024
|
+
this.#chains.set(scope, guarded);
|
|
1025
|
+
guarded.then(() => {
|
|
1026
|
+
if (this.#chains.get(scope) === guarded) this.#chains.delete(scope);
|
|
1027
|
+
});
|
|
1006
1028
|
return next;
|
|
1007
1029
|
}
|
|
1008
1030
|
get(scope, key) {
|
|
@@ -1062,7 +1084,6 @@ var RunTerminated = class extends Error {
|
|
|
1062
1084
|
/** bridge.request 自身异常(非超时):恢复 running 后转为工具错误回喂 */
|
|
1063
1085
|
var BridgeRequestError = class extends Error {};
|
|
1064
1086
|
const baseName = (p) => p.split("/").pop() ?? p;
|
|
1065
|
-
const messageOf$1 = (e) => e instanceof Error ? e.message : String(e);
|
|
1066
1087
|
const toolError = (code, message) => ({
|
|
1067
1088
|
ok: false,
|
|
1068
1089
|
content: [],
|
|
@@ -1090,6 +1111,7 @@ var AgentLoop = class {
|
|
|
1090
1111
|
totalTimeoutMs: config.totalTimeoutMs ?? 12e4,
|
|
1091
1112
|
toolTimeoutMs: config.toolTimeoutMs ?? 3e4,
|
|
1092
1113
|
toolResultMaxBytes: config.toolResultMaxBytes ?? 1e5,
|
|
1114
|
+
paramHistoryLimit: config.paramHistoryLimit ?? 50,
|
|
1093
1115
|
temperature: config.temperature,
|
|
1094
1116
|
renderResult: config.renderResult
|
|
1095
1117
|
};
|
|
@@ -1148,7 +1170,7 @@ var AgentLoop = class {
|
|
|
1148
1170
|
try {
|
|
1149
1171
|
return await source.listToolSpecs();
|
|
1150
1172
|
} catch (e) {
|
|
1151
|
-
trace.record("run.warning", { message: `External tool source "${source.kind}" failed to list specs: ${messageOf
|
|
1173
|
+
trace.record("run.warning", { message: `External tool source "${source.kind}" failed to list specs: ${messageOf(e)}` });
|
|
1152
1174
|
return [];
|
|
1153
1175
|
}
|
|
1154
1176
|
}))).flat();
|
|
@@ -1166,7 +1188,7 @@ var AgentLoop = class {
|
|
|
1166
1188
|
try {
|
|
1167
1189
|
return await this.#turnLoop(state, 1, externalSpecs);
|
|
1168
1190
|
} catch (e) {
|
|
1169
|
-
if (e instanceof WebSkillError && e.code === "RUN_FAILED") return this.#finish(state, "failed", "hook-error", messageOf
|
|
1191
|
+
if (e instanceof WebSkillError && e.code === "RUN_FAILED") return this.#finish(state, "failed", "hook-error", messageOf(e), "RUN_FAILED");
|
|
1170
1192
|
throw e;
|
|
1171
1193
|
}
|
|
1172
1194
|
}
|
|
@@ -1196,73 +1218,77 @@ var AgentLoop = class {
|
|
|
1196
1218
|
const { llm } = this.#deps;
|
|
1197
1219
|
const { trace, messages } = state;
|
|
1198
1220
|
this.#armDeadline(state);
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
response = llm.stream ? await this.#completeStreaming(llm, state, {
|
|
1217
|
-
model: this.#deps.model,
|
|
1218
|
-
messages,
|
|
1219
|
-
tools: toolSpecs,
|
|
1220
|
-
temperature: this.#config.temperature,
|
|
1221
|
-
signal: state.controller.signal
|
|
1222
|
-
}) : await llm.complete({
|
|
1223
|
-
model: this.#deps.model,
|
|
1224
|
-
messages,
|
|
1225
|
-
tools: toolSpecs,
|
|
1226
|
-
temperature: this.#config.temperature,
|
|
1227
|
-
signal: state.controller.signal
|
|
1228
|
-
});
|
|
1229
|
-
} catch (e) {
|
|
1230
|
-
if (state.controller.signal.aborted) return finish("failed", "timeout", `Agent loop exceeded the total timeout of ${state.totalTimeoutMs}ms`, "RUN_TIMEOUT");
|
|
1231
|
-
const code = e instanceof WebSkillError && (e.code === "LLM_UNAVAILABLE" || e.code === "LLM_REQUEST_FAILED") ? e.code : "LLM_REQUEST_FAILED";
|
|
1232
|
-
return finish("failed", "llm-error", messageOf$1(e), code);
|
|
1233
|
-
}
|
|
1234
|
-
trace.record("llm.response", { data: {
|
|
1235
|
-
turn,
|
|
1236
|
-
hasToolCalls: Boolean(response.toolCalls?.length),
|
|
1237
|
-
contentLength: response.content?.length ?? 0
|
|
1238
|
-
} });
|
|
1239
|
-
if (!response.toolCalls?.length) {
|
|
1240
|
-
messages.push({
|
|
1241
|
-
role: "assistant",
|
|
1242
|
-
content: response.content ?? ""
|
|
1243
|
-
});
|
|
1244
|
-
return finish("completed", "final-answer", response.content ?? "");
|
|
1245
|
-
}
|
|
1246
|
-
await this.#lifecycle("execute", state, { turn });
|
|
1247
|
-
messages.push({
|
|
1248
|
-
role: "assistant",
|
|
1249
|
-
content: response.content ?? "",
|
|
1250
|
-
toolCalls: response.toolCalls
|
|
1251
|
-
});
|
|
1252
|
-
for (const call of response.toolCalls) {
|
|
1253
|
-
let result;
|
|
1221
|
+
try {
|
|
1222
|
+
for (let turn = startTurn;; turn++) {
|
|
1223
|
+
state.turn = turn;
|
|
1224
|
+
if (turn > state.maxTurns) return finish("failed", "max-turns", `Agent loop exceeded the maximum of ${state.maxTurns} turns`, "RUN_MAX_TURNS_EXCEEDED");
|
|
1225
|
+
if (this.#elapsed(state) > state.totalTimeoutMs) return finish("failed", "timeout", `Agent loop exceeded the total timeout of ${state.totalTimeoutMs}ms`, "RUN_TIMEOUT");
|
|
1226
|
+
const toolSpecs = [
|
|
1227
|
+
toLlmToolSpec(READ_SKILL_FILE_TOOL),
|
|
1228
|
+
...this.#deps.uiBridge ? [toLlmToolSpec(ASK_USER_TOOL)] : [],
|
|
1229
|
+
...[...state.activatedTools.values()].map(toLlmToolSpec),
|
|
1230
|
+
...externalSpecs
|
|
1231
|
+
];
|
|
1232
|
+
trace.record("llm.request", { data: {
|
|
1233
|
+
turn,
|
|
1234
|
+
messageCount: messages.length,
|
|
1235
|
+
toolCount: toolSpecs.length
|
|
1236
|
+
} });
|
|
1237
|
+
let response;
|
|
1254
1238
|
try {
|
|
1255
|
-
|
|
1239
|
+
response = llm.stream ? await this.#completeStreaming(llm, state, {
|
|
1240
|
+
model: this.#deps.model,
|
|
1241
|
+
messages,
|
|
1242
|
+
tools: toolSpecs,
|
|
1243
|
+
temperature: this.#config.temperature,
|
|
1244
|
+
signal: state.controller.signal
|
|
1245
|
+
}) : await llm.complete({
|
|
1246
|
+
model: this.#deps.model,
|
|
1247
|
+
messages,
|
|
1248
|
+
tools: toolSpecs,
|
|
1249
|
+
temperature: this.#config.temperature,
|
|
1250
|
+
signal: state.controller.signal
|
|
1251
|
+
});
|
|
1256
1252
|
} catch (e) {
|
|
1257
|
-
if (
|
|
1258
|
-
|
|
1253
|
+
if (state.controller.signal.aborted) return finish("failed", "timeout", `Agent loop exceeded the total timeout of ${state.totalTimeoutMs}ms`, "RUN_TIMEOUT");
|
|
1254
|
+
const code = e instanceof WebSkillError && (e.code === "LLM_UNAVAILABLE" || e.code === "LLM_REQUEST_FAILED") ? e.code : "LLM_REQUEST_FAILED";
|
|
1255
|
+
return finish("failed", "llm-error", messageOf(e), code);
|
|
1259
1256
|
}
|
|
1257
|
+
trace.record("llm.response", { data: {
|
|
1258
|
+
turn,
|
|
1259
|
+
hasToolCalls: Boolean(response.toolCalls?.length),
|
|
1260
|
+
contentLength: response.content?.length ?? 0
|
|
1261
|
+
} });
|
|
1262
|
+
if (!response.toolCalls?.length) {
|
|
1263
|
+
messages.push({
|
|
1264
|
+
role: "assistant",
|
|
1265
|
+
content: response.content ?? ""
|
|
1266
|
+
});
|
|
1267
|
+
return finish("completed", "final-answer", response.content ?? "");
|
|
1268
|
+
}
|
|
1269
|
+
await this.#lifecycle("execute", state, { turn });
|
|
1260
1270
|
messages.push({
|
|
1261
|
-
role: "
|
|
1262
|
-
|
|
1263
|
-
|
|
1271
|
+
role: "assistant",
|
|
1272
|
+
content: response.content ?? "",
|
|
1273
|
+
toolCalls: response.toolCalls
|
|
1264
1274
|
});
|
|
1275
|
+
for (const call of response.toolCalls) {
|
|
1276
|
+
let result;
|
|
1277
|
+
try {
|
|
1278
|
+
result = await this.#executeCall(call, state);
|
|
1279
|
+
} catch (e) {
|
|
1280
|
+
if (e instanceof RunTerminated) return finish(e.outcome.status, e.outcome.reason, e.outcome.message, e.outcome.code);
|
|
1281
|
+
throw e;
|
|
1282
|
+
}
|
|
1283
|
+
messages.push({
|
|
1284
|
+
role: "tool",
|
|
1285
|
+
toolCallId: call.id,
|
|
1286
|
+
content: await this.#serializeToolResult(call, result, state)
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1265
1289
|
}
|
|
1290
|
+
} finally {
|
|
1291
|
+
this.#disarmDeadline(state);
|
|
1266
1292
|
}
|
|
1267
1293
|
}
|
|
1268
1294
|
/** 统一终态处理:completed/failed/cancelled;终态即删快照(快照是续命机制,审计归治理) */
|
|
@@ -1292,17 +1318,17 @@ var AgentLoop = class {
|
|
|
1292
1318
|
await bridge.renderResult(request);
|
|
1293
1319
|
trace.record("ui.rendered", { data: { blockCount: request.blocks.length } });
|
|
1294
1320
|
} catch (e) {
|
|
1295
|
-
trace.record("run.warning", { message: `renderResult failed: ${messageOf
|
|
1321
|
+
trace.record("run.warning", { message: `renderResult failed: ${messageOf(e)}` });
|
|
1296
1322
|
}
|
|
1297
1323
|
if (this.#deps.snapshotStore) try {
|
|
1298
1324
|
await this.#deps.snapshotStore.delete(state.runId);
|
|
1299
1325
|
} catch (e) {
|
|
1300
|
-
trace.record("run.warning", { message: `Failed to delete run snapshot: ${messageOf
|
|
1326
|
+
trace.record("run.warning", { message: `Failed to delete run snapshot: ${messageOf(e)}` });
|
|
1301
1327
|
}
|
|
1302
1328
|
try {
|
|
1303
1329
|
await this.#lifecycle(status === "completed" ? "complete" : "fail", state, { reason });
|
|
1304
1330
|
} catch (e) {
|
|
1305
|
-
trace.record("run.warning", { message: `Terminal lifecycle hook failed: ${messageOf
|
|
1331
|
+
trace.record("run.warning", { message: `Terminal lifecycle hook failed: ${messageOf(e)}` });
|
|
1306
1332
|
}
|
|
1307
1333
|
run.trace = trace.list();
|
|
1308
1334
|
return {
|
|
@@ -1329,6 +1355,7 @@ var AgentLoop = class {
|
|
|
1329
1355
|
interactionExpiresAt: state.run.interruptExpiresAt,
|
|
1330
1356
|
renderBlocks: [...state.renderBlocks],
|
|
1331
1357
|
interactionSeq: state.interactionSeq,
|
|
1358
|
+
pausedMs: state.pausedMs,
|
|
1332
1359
|
config: {
|
|
1333
1360
|
maxTurns: state.maxTurns,
|
|
1334
1361
|
totalTimeoutMs: state.totalTimeoutMs,
|
|
@@ -1340,7 +1367,7 @@ var AgentLoop = class {
|
|
|
1340
1367
|
try {
|
|
1341
1368
|
await store.save(snapshot);
|
|
1342
1369
|
} catch (e) {
|
|
1343
|
-
state.trace.record("run.warning", { message: `Failed to save run snapshot: ${messageOf
|
|
1370
|
+
state.trace.record("run.warning", { message: `Failed to save run snapshot: ${messageOf(e)}` });
|
|
1344
1371
|
}
|
|
1345
1372
|
}
|
|
1346
1373
|
/**
|
|
@@ -1376,7 +1403,7 @@ var AgentLoop = class {
|
|
|
1376
1403
|
turn: snapshot.turn,
|
|
1377
1404
|
renderBlocks: (snapshot.renderBlocks ?? []).map((b) => ({ ...b })),
|
|
1378
1405
|
startMs,
|
|
1379
|
-
pausedMs: 0,
|
|
1406
|
+
pausedMs: snapshot.pausedMs ?? 0,
|
|
1380
1407
|
maxTurns: snapshot.config.maxTurns,
|
|
1381
1408
|
totalTimeoutMs: snapshot.config.totalTimeoutMs,
|
|
1382
1409
|
controller: new AbortController()
|
|
@@ -1455,14 +1482,14 @@ var AgentLoop = class {
|
|
|
1455
1482
|
try {
|
|
1456
1483
|
return await source.listToolSpecs();
|
|
1457
1484
|
} catch (e) {
|
|
1458
|
-
trace.record("run.warning", { message: `External tool source "${source.kind}" failed to list specs: ${messageOf
|
|
1485
|
+
trace.record("run.warning", { message: `External tool source "${source.kind}" failed to list specs: ${messageOf(e)}` });
|
|
1459
1486
|
return [];
|
|
1460
1487
|
}
|
|
1461
1488
|
}))).flat();
|
|
1462
1489
|
try {
|
|
1463
1490
|
return await this.#turnLoop(state, snapshot.turn + 1, externalSpecs);
|
|
1464
1491
|
} catch (e) {
|
|
1465
|
-
if (e instanceof WebSkillError && e.code === "RUN_FAILED") return this.#finish(state, "failed", "hook-error", messageOf
|
|
1492
|
+
if (e instanceof WebSkillError && e.code === "RUN_FAILED") return this.#finish(state, "failed", "hook-error", messageOf(e), "RUN_FAILED");
|
|
1466
1493
|
throw e;
|
|
1467
1494
|
}
|
|
1468
1495
|
}
|
|
@@ -1564,8 +1591,8 @@ var AgentLoop = class {
|
|
|
1564
1591
|
message: e.message,
|
|
1565
1592
|
code: "RUN_INTERACTION_TIMEOUT"
|
|
1566
1593
|
});
|
|
1567
|
-
state.trace.record("run.warning", { message: `UiBridge request failed: ${messageOf
|
|
1568
|
-
throw new BridgeRequestError(messageOf
|
|
1594
|
+
state.trace.record("run.warning", { message: `UiBridge request failed: ${messageOf(e)}` });
|
|
1595
|
+
throw new BridgeRequestError(messageOf(e));
|
|
1569
1596
|
}
|
|
1570
1597
|
if (response.cancelled) throw new RunTerminated({
|
|
1571
1598
|
status: "cancelled",
|
|
@@ -1684,7 +1711,7 @@ var AgentLoop = class {
|
|
|
1684
1711
|
};
|
|
1685
1712
|
} catch (e) {
|
|
1686
1713
|
if (e instanceof WebSkillError && e.code === "SKILL_NOT_FOUND" && (this.#deps.skillProviders?.length ?? 0) > 0) return this.#handleReadExternalSkill(skillName, pathArg, state);
|
|
1687
|
-
return toolError(e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED", `read_skill_file failed: ${messageOf
|
|
1714
|
+
return toolError(e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED", `read_skill_file failed: ${messageOf(e)}`);
|
|
1688
1715
|
}
|
|
1689
1716
|
}
|
|
1690
1717
|
/** 经外部技能提供者读取技能文档/资源;skillKey 为技能名或 mcp:// URI */
|
|
@@ -1724,7 +1751,7 @@ var AgentLoop = class {
|
|
|
1724
1751
|
}]
|
|
1725
1752
|
};
|
|
1726
1753
|
} catch (e) {
|
|
1727
|
-
failures.push(messageOf
|
|
1754
|
+
failures.push(messageOf(e));
|
|
1728
1755
|
continue;
|
|
1729
1756
|
}
|
|
1730
1757
|
const detail = failures.length > 0 ? ` (provider errors: ${failures.join("; ")})` : "";
|
|
@@ -1746,7 +1773,7 @@ var AgentLoop = class {
|
|
|
1746
1773
|
content: text
|
|
1747
1774
|
})).id}"`;
|
|
1748
1775
|
} catch (e) {
|
|
1749
|
-
state.trace.record("run.warning", { message: `Failed to persist oversized tool result artifact for "${call.name}": ${messageOf
|
|
1776
|
+
state.trace.record("run.warning", { message: `Failed to persist oversized tool result artifact for "${call.name}": ${messageOf(e)}` });
|
|
1750
1777
|
note = "full result discarded (artifact store unavailable)";
|
|
1751
1778
|
}
|
|
1752
1779
|
const head = text.slice(0, Math.floor(max * .6));
|
|
@@ -1784,7 +1811,7 @@ var AgentLoop = class {
|
|
|
1784
1811
|
if (rawAllowed !== void 0) if (Array.isArray(rawAllowed)) allowedTools = rawAllowed.filter((e) => typeof e === "string");
|
|
1785
1812
|
else state.trace.record("run.warning", { message: `Skill "${skillName}" has a non-array "allowed-tools" metadata entry; ignored` });
|
|
1786
1813
|
} catch (e) {
|
|
1787
|
-
state.trace.record("run.warning", { message: `Failed to read or parse SKILL.md of skill "${skillName}": ${messageOf
|
|
1814
|
+
state.trace.record("run.warning", { message: `Failed to read or parse SKILL.md of skill "${skillName}": ${messageOf(e)}` });
|
|
1788
1815
|
}
|
|
1789
1816
|
const executor = this.#deps.executor;
|
|
1790
1817
|
const loaded = [];
|
|
@@ -1805,7 +1832,7 @@ var AgentLoop = class {
|
|
|
1805
1832
|
state.activatedTools.set(def.name, def);
|
|
1806
1833
|
loaded.push(def.name);
|
|
1807
1834
|
} catch (e) {
|
|
1808
|
-
state.trace.record("run.warning", { message: `Failed to load definition of script "${match[1]}" for skill "${skillName}": ${messageOf
|
|
1835
|
+
state.trace.record("run.warning", { message: `Failed to load definition of script "${match[1]}" for skill "${skillName}": ${messageOf(e)}` });
|
|
1809
1836
|
}
|
|
1810
1837
|
}
|
|
1811
1838
|
}
|
|
@@ -1831,7 +1858,7 @@ var AgentLoop = class {
|
|
|
1831
1858
|
def.inputSchema = JSON.parse(await this.#deps.fs.readText(sidecar));
|
|
1832
1859
|
return;
|
|
1833
1860
|
} catch (e) {
|
|
1834
|
-
state.trace.record("run.warning", { message: `Failed to parse schema sidecar ${sidecar}: ${messageOf
|
|
1861
|
+
state.trace.record("run.warning", { message: `Failed to parse schema sidecar ${sidecar}: ${messageOf(e)}` });
|
|
1835
1862
|
}
|
|
1836
1863
|
if (!this.#deps.schemaInferer) return;
|
|
1837
1864
|
for (const ext of ["ts", "js"]) {
|
|
@@ -1841,7 +1868,7 @@ var AgentLoop = class {
|
|
|
1841
1868
|
const inferred = this.#deps.schemaInferer.inferSchemaFromSource(await this.#deps.fs.readText(scriptPath), { fileName: `${scriptName}.${ext}` });
|
|
1842
1869
|
if (inferred) def.inputSchema = inferred;
|
|
1843
1870
|
} catch (e) {
|
|
1844
|
-
state.trace.record("run.warning", { message: `Schema inference failed for ${scriptPath}: ${messageOf
|
|
1871
|
+
state.trace.record("run.warning", { message: `Schema inference failed for ${scriptPath}: ${messageOf(e)}` });
|
|
1845
1872
|
}
|
|
1846
1873
|
return;
|
|
1847
1874
|
}
|
|
@@ -1902,7 +1929,7 @@ var AgentLoop = class {
|
|
|
1902
1929
|
} catch (e) {
|
|
1903
1930
|
if (e instanceof RunTerminated) throw e;
|
|
1904
1931
|
await this.#bumpSkillStat(skillName, "failures", state);
|
|
1905
|
-
return toolError(e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED", `Tool "${call.name}" failed: ${messageOf
|
|
1932
|
+
return toolError(e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED", `Tool "${call.name}" failed: ${messageOf(e)}`);
|
|
1906
1933
|
}
|
|
1907
1934
|
}
|
|
1908
1935
|
/** context.confirm 触发点:默认真实询问;auto-approve 直通;无 bridge 降级直通 + warning */
|
|
@@ -1941,7 +1968,7 @@ var AgentLoop = class {
|
|
|
1941
1968
|
try {
|
|
1942
1969
|
await this.#deps.memory?.set(scope, key, value);
|
|
1943
1970
|
} catch (e) {
|
|
1944
|
-
state.trace.record("run.warning", { message: `Memory write failed: ${messageOf
|
|
1971
|
+
state.trace.record("run.warning", { message: `Memory write failed: ${messageOf(e)}` });
|
|
1945
1972
|
}
|
|
1946
1973
|
}
|
|
1947
1974
|
/** read-modify-write:有 transaction 实现在 scope 原子段内执行(并发不丢计数),否则顺序执行 */
|
|
@@ -1954,7 +1981,7 @@ var AgentLoop = class {
|
|
|
1954
1981
|
await inner.set(scope, key, mutate(await inner.get(scope, key)));
|
|
1955
1982
|
});
|
|
1956
1983
|
} catch (e) {
|
|
1957
|
-
state.trace.record("run.warning", { message: `Memory write failed: ${messageOf
|
|
1984
|
+
state.trace.record("run.warning", { message: `Memory write failed: ${messageOf(e)}` });
|
|
1958
1985
|
}
|
|
1959
1986
|
return;
|
|
1960
1987
|
}
|
|
@@ -1989,6 +2016,7 @@ var AgentLoop = class {
|
|
|
1989
2016
|
async #appendParamHistory(state, request, value) {
|
|
1990
2017
|
if (!this.#deps.memory) return;
|
|
1991
2018
|
const scope = `session:${state.run.sessionId}`;
|
|
2019
|
+
const limit = this.#config.paramHistoryLimit;
|
|
1992
2020
|
await this.#memoryMutate(scope, "paramHistory", state, (current) => {
|
|
1993
2021
|
const history = current ?? [];
|
|
1994
2022
|
history.push({
|
|
@@ -1997,7 +2025,7 @@ var AgentLoop = class {
|
|
|
1997
2025
|
type: request.type,
|
|
1998
2026
|
value
|
|
1999
2027
|
});
|
|
2000
|
-
return history;
|
|
2028
|
+
return history.slice(-limit);
|
|
2001
2029
|
});
|
|
2002
2030
|
}
|
|
2003
2031
|
};
|
|
@@ -2084,6 +2112,24 @@ var FsRunSnapshotStore = class {
|
|
|
2084
2112
|
}
|
|
2085
2113
|
};
|
|
2086
2114
|
/**
|
|
2115
|
+
* session history 滚动裁剪:超出预算时裁掉中段,保留首尾。
|
|
2116
|
+
* 边界对齐 LLM tool 契约:head 不以未应答的 assistant toolCalls 结尾,
|
|
2117
|
+
* tail 不以孤儿 tool 消息开头(其 assistant 调用已随中段裁掉)。
|
|
2118
|
+
*/
|
|
2119
|
+
function trimSessionHistory(messages, max) {
|
|
2120
|
+
if (max < 2 || messages.length <= max) return messages;
|
|
2121
|
+
let head = Math.ceil(max / 2);
|
|
2122
|
+
while (head > 0) {
|
|
2123
|
+
const last = messages[head - 1];
|
|
2124
|
+
if (last.role === "assistant" && last.toolCalls?.length) head -= 1;
|
|
2125
|
+
else break;
|
|
2126
|
+
}
|
|
2127
|
+
let tailStart = Math.max(head, messages.length - (max - head));
|
|
2128
|
+
while (tailStart < messages.length && messages[tailStart].role === "tool") tailStart += 1;
|
|
2129
|
+
if (tailStart >= messages.length) return messages.slice(-max);
|
|
2130
|
+
return [...messages.slice(0, head), ...messages.slice(tailStart)];
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2087
2133
|
* runtime 门面:组合 discovery / router / agent loop / lifecycle
|
|
2088
2134
|
* @stable
|
|
2089
2135
|
*/
|
|
@@ -2128,20 +2174,28 @@ var WebSkillRuntime = class {
|
|
|
2128
2174
|
/**
|
|
2129
2175
|
* 多会话:session 对象的 run(prompt) 跨 run 延续消息历史(同一 session 对话上下文连续)。
|
|
2130
2176
|
* 既有 runtime.run(prompt) 保持无状态单次语义不变。
|
|
2177
|
+
* 同 handle 并发 run 经 per-handle 队列串行化(防历史 last-writer-wins 丢轮次);
|
|
2178
|
+
* maxHistoryMessages(默认 100)超出时滚动裁剪中段(保留首尾;边界对齐 tool 契约)。
|
|
2131
2179
|
*/
|
|
2132
2180
|
createSession(options = {}) {
|
|
2133
2181
|
const sessionId = options.sessionId ?? `session-${Math.random().toString(36).slice(2, 10)}`;
|
|
2134
2182
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2183
|
+
const maxHistory = options.maxHistoryMessages ?? 100;
|
|
2135
2184
|
let history = [];
|
|
2185
|
+
let queue = Promise.resolve();
|
|
2136
2186
|
return {
|
|
2137
2187
|
id: sessionId,
|
|
2138
2188
|
createdAt,
|
|
2139
|
-
run:
|
|
2140
|
-
const result =
|
|
2141
|
-
|
|
2142
|
-
|
|
2189
|
+
run: (prompt) => {
|
|
2190
|
+
const result = queue.then(async () => {
|
|
2191
|
+
const r = await this.run(prompt, {
|
|
2192
|
+
sessionId,
|
|
2193
|
+
history
|
|
2194
|
+
});
|
|
2195
|
+
history = trimSessionHistory(r.messages.slice(1), maxHistory);
|
|
2196
|
+
return r;
|
|
2143
2197
|
});
|
|
2144
|
-
|
|
2198
|
+
queue = result.then(() => void 0, () => void 0);
|
|
2145
2199
|
return result;
|
|
2146
2200
|
}
|
|
2147
2201
|
};
|
|
@@ -2389,7 +2443,6 @@ function sourceFromUrl(url) {
|
|
|
2389
2443
|
path: url
|
|
2390
2444
|
};
|
|
2391
2445
|
}
|
|
2392
|
-
const messageOf = (e) => e instanceof Error ? e.message : String(e);
|
|
2393
2446
|
/** 受控钩子执行器:逐个执行,超时/异常默认降级为 warning,可切严格模式 */
|
|
2394
2447
|
var HookRunner = class {
|
|
2395
2448
|
#hooks = /* @__PURE__ */ new Map();
|