@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.
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +1 -1
- package/dist/catalogComponents-C_V39rbF-B94i0fW7.js +129139 -0
- package/dist/{dist-B77plHjw.js → dist-BdOW8N4V.js} +505 -35
- package/dist/{dist-Chk8iB-E.js → dist-CcMIUXeZ.js} +1 -1
- package/dist/{dist-DNSG9FqC.js → dist-CtBLBbEz.js} +1186 -123
- package/dist/dist-DxyxmXPU-DGC-dpXf.js +5621 -0
- package/dist/echarts-DhNm2ene.js +78816 -0
- package/dist/governance.d.ts +94 -93
- package/dist/governance.js +205 -194
- package/dist/{index-CsDJvYGV.d.ts → index-BHL5FWGw.d.ts} +2 -2
- package/dist/index-BNQNSDKg.d.ts +580 -0
- package/dist/{index-DfINBEOy.d.ts → index-BpIK7tJM.d.ts} +35 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/jsonRenderRegistry-9GrWP_hE-CMkryt1H.js +2468 -0
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +1 -1
- package/dist/node.d.ts +3 -3
- package/dist/node.js +2 -2
- package/dist/openUiLibrary-B8-Cvou9-Cu1QZqoT.js +3785 -0
- package/dist/rolldown-runtime-BOF7iYI8.js +55 -0
- package/dist/testing.d.ts +1 -1
- package/dist/{types-CKm5G_eQ-krKWW8WV.d.ts → types-7fnqDVrf-BnRQjVU3.d.ts} +127 -2
- package/dist/ui-react.d.ts +138 -2
- package/dist/ui-react.js +6935 -3
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui-vue.js +1 -1
- package/dist/ui.d.ts +3 -203
- package/dist/ui.js +3 -3
- package/dist/webskillLitCatalog-CNaUpasU-CfSRvqCZ.js +1601 -0
- package/package.json +2 -1
|
@@ -40,7 +40,7 @@ var OpenAiCompatibleClient = class {
|
|
|
40
40
|
#fetch;
|
|
41
41
|
constructor(config) {
|
|
42
42
|
this.#config = config;
|
|
43
|
-
this.#fetch = config.fetchImpl ?? fetch;
|
|
43
|
+
this.#fetch = config.fetchImpl ?? ((input, init) => fetch(input, init));
|
|
44
44
|
}
|
|
45
45
|
#requireConfig() {
|
|
46
46
|
const { baseUrl, apiKey, model } = this.#config;
|
|
@@ -262,7 +262,7 @@ var AnthropicClient = class {
|
|
|
262
262
|
#fetch;
|
|
263
263
|
constructor(config) {
|
|
264
264
|
this.#config = config;
|
|
265
|
-
this.#fetch = config.fetchImpl ?? fetch;
|
|
265
|
+
this.#fetch = config.fetchImpl ?? ((input, init) => fetch(input, init));
|
|
266
266
|
}
|
|
267
267
|
#baseUrl() {
|
|
268
268
|
return (this.#config.baseUrl ?? "https://api.anthropic.com").replace(/\/+$/, "");
|
|
@@ -491,7 +491,7 @@ var GoogleGenAiClient = class {
|
|
|
491
491
|
#fetch;
|
|
492
492
|
constructor(config) {
|
|
493
493
|
this.#config = config;
|
|
494
|
-
this.#fetch = config.fetchImpl ?? fetch;
|
|
494
|
+
this.#fetch = config.fetchImpl ?? ((input, init) => fetch(input, init));
|
|
495
495
|
}
|
|
496
496
|
#baseUrl() {
|
|
497
497
|
return (this.#config.baseUrl ?? "https://generativelanguage.googleapis.com").replace(/\/+$/, "");
|
|
@@ -875,22 +875,22 @@ function createScriptContext(deps) {
|
|
|
875
875
|
...onWarning ? { onWarning } : {}
|
|
876
876
|
};
|
|
877
877
|
}
|
|
878
|
-
const isRecord$
|
|
878
|
+
const isRecord$2 = (v) => typeof v === "object" && v !== null;
|
|
879
879
|
/**
|
|
880
880
|
* $chart 约定的形状校验:JSON content 的 data 含 $chart 键且形状合法 → ChartSpec;
|
|
881
881
|
* 任何畸形(kind 非法 / labels 非字符串数组 / series 项缺数值 data)→ undefined(忽略不炸)。
|
|
882
882
|
*/
|
|
883
883
|
function extractChartSpec(data) {
|
|
884
|
-
if (!isRecord$
|
|
884
|
+
if (!isRecord$2(data)) return void 0;
|
|
885
885
|
const raw = data["$chart"];
|
|
886
|
-
if (!isRecord$
|
|
886
|
+
if (!isRecord$2(raw)) return void 0;
|
|
887
887
|
const { kind, labels, series } = raw;
|
|
888
888
|
if (kind !== "bar" && kind !== "line" && kind !== "pie") return void 0;
|
|
889
889
|
if (!Array.isArray(labels) || !labels.every((l) => typeof l === "string")) return void 0;
|
|
890
890
|
if (!Array.isArray(series)) return void 0;
|
|
891
891
|
const validSeries = [];
|
|
892
892
|
for (const item of series) {
|
|
893
|
-
if (!isRecord$
|
|
893
|
+
if (!isRecord$2(item) || !Array.isArray(item["data"]) || !item["data"].every((n) => typeof n === "number")) return;
|
|
894
894
|
const name = item["name"];
|
|
895
895
|
validSeries.push({
|
|
896
896
|
...typeof name === "string" ? { name } : {},
|
|
@@ -928,6 +928,212 @@ function buildRenderResult(run, output, renderBlocks = []) {
|
|
|
928
928
|
artifacts: run.artifacts
|
|
929
929
|
};
|
|
930
930
|
}
|
|
931
|
+
const MAX_SURFACE_BYTES = 256 * 1024;
|
|
932
|
+
const MAX_ACTIONS = 32;
|
|
933
|
+
const MAX_FORM_FIELDS = 64;
|
|
934
|
+
const MAX_OPTIONS = 256;
|
|
935
|
+
const MAX_TABLE_COLUMNS = 128;
|
|
936
|
+
const MAX_TABLE_ROWS = 1e4;
|
|
937
|
+
const MAX_CHART_POINTS = 2e4;
|
|
938
|
+
const MAX_JSON_DEPTH = 16;
|
|
939
|
+
const MAX_PATCH_OPERATIONS = 128;
|
|
940
|
+
const actionIntents = /* @__PURE__ */ new Set([
|
|
941
|
+
"submit",
|
|
942
|
+
"cancel",
|
|
943
|
+
"select",
|
|
944
|
+
"download",
|
|
945
|
+
"refresh"
|
|
946
|
+
]);
|
|
947
|
+
const fieldTypes = /* @__PURE__ */ new Set([
|
|
948
|
+
"text",
|
|
949
|
+
"number",
|
|
950
|
+
"date",
|
|
951
|
+
"textarea",
|
|
952
|
+
"select",
|
|
953
|
+
"multi-select",
|
|
954
|
+
"toggle",
|
|
955
|
+
"file"
|
|
956
|
+
]);
|
|
957
|
+
function isRecord$1(value) {
|
|
958
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
959
|
+
}
|
|
960
|
+
function reject(message) {
|
|
961
|
+
throw new WebSkillError("VALIDATION_FAILED", message);
|
|
962
|
+
}
|
|
963
|
+
function requireString(value, name) {
|
|
964
|
+
if (typeof value !== "string" || value.trim() === "") reject(`${name} must be a non-empty string`);
|
|
965
|
+
}
|
|
966
|
+
function requireNonNegativeInteger(value, name) {
|
|
967
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) reject(`${name} must be a non-negative integer`);
|
|
968
|
+
}
|
|
969
|
+
function isJsonValue(value, depth = 0) {
|
|
970
|
+
if (depth > MAX_JSON_DEPTH || value === void 0 || typeof value === "function" || typeof value === "symbol") return false;
|
|
971
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
972
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
973
|
+
if (Array.isArray(value)) return value.every((item) => isJsonValue(item, depth + 1));
|
|
974
|
+
if (!isRecord$1(value)) return false;
|
|
975
|
+
return Object.keys(value).every((key) => key !== "__proto__" && key !== "constructor" && isJsonValue(value[key], depth + 1));
|
|
976
|
+
}
|
|
977
|
+
function assertActions(value) {
|
|
978
|
+
if (value === void 0) return;
|
|
979
|
+
if (!Array.isArray(value) || value.length > MAX_ACTIONS) reject(`Surface actions must contain at most ${MAX_ACTIONS} items`);
|
|
980
|
+
for (const action of value) {
|
|
981
|
+
if (!isRecord$1(action)) reject("A surface action must be an object");
|
|
982
|
+
requireString(action["id"], "Surface action ID");
|
|
983
|
+
requireString(action["label"], "Surface action label");
|
|
984
|
+
if (typeof action["intent"] !== "string" || !actionIntents.has(action["intent"])) reject("Surface action intent is invalid");
|
|
985
|
+
if (action["disabled"] !== void 0 && typeof action["disabled"] !== "boolean") reject("Surface action disabled must be a boolean");
|
|
986
|
+
if (action["awaitResponse"] !== void 0 && typeof action["awaitResponse"] !== "boolean") reject("Surface action awaitResponse must be a boolean");
|
|
987
|
+
if (action["nonce"] !== void 0 && (typeof action["nonce"] !== "string" || action["nonce"] === "")) reject("Surface action nonce must be a non-empty string");
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
function assertForm(surface) {
|
|
991
|
+
if (!Array.isArray(surface["fields"]) || surface["fields"].length > MAX_FORM_FIELDS) reject(`A form surface must contain at most ${MAX_FORM_FIELDS} fields`);
|
|
992
|
+
for (const field of surface["fields"]) {
|
|
993
|
+
if (!isRecord$1(field)) reject("A form field must be an object");
|
|
994
|
+
requireString(field["name"], "Form field name");
|
|
995
|
+
requireString(field["label"], "Form field label");
|
|
996
|
+
if (typeof field["type"] !== "string" || !fieldTypes.has(field["type"])) reject("Form field type is invalid");
|
|
997
|
+
if (field["required"] !== void 0 && typeof field["required"] !== "boolean") reject("Form field required must be a boolean");
|
|
998
|
+
if (field["description"] !== void 0 && typeof field["description"] !== "string") reject("Form field description must be a string");
|
|
999
|
+
if (field["defaultValue"] !== void 0 && !isJsonValue(field["defaultValue"])) reject("Form field defaultValue must be JSON data");
|
|
1000
|
+
if (field["options"] !== void 0) {
|
|
1001
|
+
if (!Array.isArray(field["options"]) || field["options"].length > MAX_OPTIONS) reject(`Form field options must contain at most ${MAX_OPTIONS} items`);
|
|
1002
|
+
for (const option of field["options"]) {
|
|
1003
|
+
if (!isRecord$1(option)) reject("A form option must be an object");
|
|
1004
|
+
requireString(option["label"], "Form option label");
|
|
1005
|
+
if (!isJsonValue(option["value"])) reject("Form option value must be JSON data");
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
assertActions(surface["actions"]);
|
|
1010
|
+
}
|
|
1011
|
+
function assertChart(surface) {
|
|
1012
|
+
const chart = surface["chart"];
|
|
1013
|
+
if (!isRecord$1(chart)) reject("A chart surface requires a chart object");
|
|
1014
|
+
if (chart["kind"] !== "bar" && chart["kind"] !== "line" && chart["kind"] !== "pie") reject("Chart kind is invalid");
|
|
1015
|
+
if (!Array.isArray(chart["labels"]) || !chart["labels"].every((label) => typeof label === "string")) reject("Chart labels must be an array of strings");
|
|
1016
|
+
if (!Array.isArray(chart["series"])) reject("Chart series must be an array");
|
|
1017
|
+
let points = 0;
|
|
1018
|
+
for (const series of chart["series"]) {
|
|
1019
|
+
if (!isRecord$1(series) || !Array.isArray(series["data"]) || !series["data"].every((point) => typeof point === "number" && Number.isFinite(point))) reject("Chart series data must be finite numbers");
|
|
1020
|
+
if (series["name"] !== void 0 && typeof series["name"] !== "string") reject("Chart series name must be a string");
|
|
1021
|
+
points += series["data"].length;
|
|
1022
|
+
}
|
|
1023
|
+
if (points > MAX_CHART_POINTS) reject(`Chart data exceeds the ${MAX_CHART_POINTS}-point limit`);
|
|
1024
|
+
assertActions(surface["actions"]);
|
|
1025
|
+
}
|
|
1026
|
+
function assertTable(surface) {
|
|
1027
|
+
if (!Array.isArray(surface["columns"]) || surface["columns"].length > MAX_TABLE_COLUMNS || !surface["columns"].every((column) => typeof column === "string")) reject(`Table columns must be strings and contain at most ${MAX_TABLE_COLUMNS} items`);
|
|
1028
|
+
if (!Array.isArray(surface["rows"]) || surface["rows"].length > MAX_TABLE_ROWS) reject(`Table rows must contain at most ${MAX_TABLE_ROWS} items`);
|
|
1029
|
+
for (const row of surface["rows"]) if (!Array.isArray(row) || row.length > surface["columns"].length || !row.every((cell) => isJsonValue(cell))) reject("Table rows must contain JSON cells within the declared column count");
|
|
1030
|
+
assertActions(surface["actions"]);
|
|
1031
|
+
}
|
|
1032
|
+
/** Validates the allowlisted, data-only shape accepted by a UI surface renderer. @experimental */
|
|
1033
|
+
function validateUiSurface(value) {
|
|
1034
|
+
if (!isRecord$1(value)) reject("A UI surface must be an object");
|
|
1035
|
+
requireString(value["id"], "UI surface ID");
|
|
1036
|
+
if (value["title"] !== void 0 && typeof value["title"] !== "string") reject("UI surface title must be a string");
|
|
1037
|
+
switch (value["kind"]) {
|
|
1038
|
+
case "form":
|
|
1039
|
+
assertForm(value);
|
|
1040
|
+
break;
|
|
1041
|
+
case "chart":
|
|
1042
|
+
assertChart(value);
|
|
1043
|
+
break;
|
|
1044
|
+
case "table":
|
|
1045
|
+
assertTable(value);
|
|
1046
|
+
break;
|
|
1047
|
+
case "metric":
|
|
1048
|
+
requireString(value["label"], "Metric label");
|
|
1049
|
+
if (typeof value["value"] !== "string" && (typeof value["value"] !== "number" || !Number.isFinite(value["value"]))) reject("Metric value must be a string or finite number");
|
|
1050
|
+
if (value["trend"] !== void 0 && value["trend"] !== "up" && value["trend"] !== "down" && value["trend"] !== "neutral") reject("Metric trend is invalid");
|
|
1051
|
+
break;
|
|
1052
|
+
case "file": {
|
|
1053
|
+
requireString(value["path"], "File path");
|
|
1054
|
+
if (value["mimeType"] !== void 0 && typeof value["mimeType"] !== "string") reject("File mimeType must be a string");
|
|
1055
|
+
const fileSize = value["size"];
|
|
1056
|
+
if (fileSize !== void 0 && (typeof fileSize !== "number" || !Number.isSafeInteger(fileSize) || fileSize < 0)) reject("File size must be a non-negative integer");
|
|
1057
|
+
assertActions(value["actions"]);
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
1060
|
+
case "custom":
|
|
1061
|
+
requireString(value["component"], "Custom surface component");
|
|
1062
|
+
if (!isJsonValue(value["props"])) reject("Custom surface props must be JSON data");
|
|
1063
|
+
assertActions(value["actions"]);
|
|
1064
|
+
break;
|
|
1065
|
+
default: reject("UI surface kind is invalid");
|
|
1066
|
+
}
|
|
1067
|
+
if (!isJsonValue(value)) reject("A UI surface must contain JSON data only");
|
|
1068
|
+
if (JSON.stringify(value).length > MAX_SURFACE_BYTES) reject(`A UI surface exceeds the ${MAX_SURFACE_BYTES}-byte limit`);
|
|
1069
|
+
return structuredClone(value);
|
|
1070
|
+
}
|
|
1071
|
+
function assertPatch(value) {
|
|
1072
|
+
if (!isRecord$1(value)) reject("A surface patch operation must be an object");
|
|
1073
|
+
if (value["op"] !== "replace" && value["op"] !== "merge" && value["op"] !== "append") reject("Surface patch operation is invalid");
|
|
1074
|
+
requireString(value["path"], "Surface patch path");
|
|
1075
|
+
if (!value["path"].startsWith("/")) reject("Surface patch path must be a JSON pointer");
|
|
1076
|
+
if (!isJsonValue(value["value"])) reject("Surface patch value must be JSON data");
|
|
1077
|
+
}
|
|
1078
|
+
/** Validates an individual event in the framework-neutral surface stream. @experimental */
|
|
1079
|
+
function validateUiSurfaceEvent(value) {
|
|
1080
|
+
if (!isRecord$1(value) || typeof value["type"] !== "string") reject("A UI surface event must have a type");
|
|
1081
|
+
if (value["runId"] !== void 0) requireString(value["runId"], "Surface event run ID");
|
|
1082
|
+
switch (value["type"]) {
|
|
1083
|
+
case "open": return {
|
|
1084
|
+
type: "open",
|
|
1085
|
+
...value["runId"] ? { runId: value["runId"] } : {},
|
|
1086
|
+
surface: validateUiSurface(value["surface"])
|
|
1087
|
+
};
|
|
1088
|
+
case "patch":
|
|
1089
|
+
requireString(value["id"], "Surface patch ID");
|
|
1090
|
+
requireNonNegativeInteger(value["revision"], "Surface patch revision");
|
|
1091
|
+
if (!Array.isArray(value["operations"]) || value["operations"].length === 0) reject("A surface patch requires operations");
|
|
1092
|
+
if (value["operations"].length > MAX_PATCH_OPERATIONS) reject(`A surface patch must contain at most ${MAX_PATCH_OPERATIONS} operations`);
|
|
1093
|
+
for (const operation of value["operations"]) assertPatch(operation);
|
|
1094
|
+
return {
|
|
1095
|
+
type: "patch",
|
|
1096
|
+
...value["runId"] ? { runId: value["runId"] } : {},
|
|
1097
|
+
id: value["id"],
|
|
1098
|
+
revision: value["revision"],
|
|
1099
|
+
operations: structuredClone(value["operations"])
|
|
1100
|
+
};
|
|
1101
|
+
case "complete":
|
|
1102
|
+
requireString(value["id"], "Surface completion ID");
|
|
1103
|
+
requireNonNegativeInteger(value["revision"], "Surface completion revision");
|
|
1104
|
+
return {
|
|
1105
|
+
type: "complete",
|
|
1106
|
+
...value["runId"] ? { runId: value["runId"] } : {},
|
|
1107
|
+
id: value["id"],
|
|
1108
|
+
revision: value["revision"]
|
|
1109
|
+
};
|
|
1110
|
+
case "error":
|
|
1111
|
+
requireString(value["id"], "Surface error ID");
|
|
1112
|
+
requireString(value["code"], "Surface error code");
|
|
1113
|
+
requireString(value["message"], "Surface error message");
|
|
1114
|
+
return {
|
|
1115
|
+
type: "error",
|
|
1116
|
+
...value["runId"] ? { runId: value["runId"] } : {},
|
|
1117
|
+
id: value["id"],
|
|
1118
|
+
code: value["code"],
|
|
1119
|
+
message: value["message"]
|
|
1120
|
+
};
|
|
1121
|
+
case "cancel":
|
|
1122
|
+
requireString(value["id"], "Surface cancellation ID");
|
|
1123
|
+
return {
|
|
1124
|
+
type: "cancel",
|
|
1125
|
+
...value["runId"] ? { runId: value["runId"] } : {},
|
|
1126
|
+
id: value["id"]
|
|
1127
|
+
};
|
|
1128
|
+
default: return reject("UI surface event type is invalid");
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
/** Extracts validated surface stream events from structured tool output. @experimental */
|
|
1132
|
+
function extractUiSurfaceEvents(data) {
|
|
1133
|
+
if (!isRecord$1(data) || data["$surface"] === void 0) return [];
|
|
1134
|
+
const raw = data["$surface"];
|
|
1135
|
+
return (Array.isArray(raw) ? raw : [raw]).map((event) => validateUiSurfaceEvent(event));
|
|
1136
|
+
}
|
|
931
1137
|
/**
|
|
932
1138
|
* JsonSchema → 表单模型:按 properties 生成字段,required 标记必填;
|
|
933
1139
|
* providedArgs 已有的值作为 defaultValue 预填(表单只为补齐缺失项服务)。
|
|
@@ -1073,6 +1279,7 @@ var TraceRecorder = class {
|
|
|
1073
1279
|
return [...this.#events];
|
|
1074
1280
|
}
|
|
1075
1281
|
};
|
|
1282
|
+
const MAX_SURFACE_PATCHES_PER_SECOND = 240;
|
|
1076
1283
|
/** 交互终态(取消/超时):从工具执行深处直接终止 run */
|
|
1077
1284
|
var RunTerminated = class extends Error {
|
|
1078
1285
|
outcome;
|
|
@@ -1092,6 +1299,12 @@ const toolError = (code, message) => ({
|
|
|
1092
1299
|
message
|
|
1093
1300
|
}
|
|
1094
1301
|
});
|
|
1302
|
+
/** 工具参数摘要(JSON 截断 100 字符;trace 与实时事件同一口径,ToolCallCard 展开详情用) */
|
|
1303
|
+
const summarizeArgs = (args) => {
|
|
1304
|
+
const json = JSON.stringify(args);
|
|
1305
|
+
return json.length > 100 ? `${json.slice(0, 100)}…` : json;
|
|
1306
|
+
};
|
|
1307
|
+
const surfaceActions = (surface) => "actions" in surface ? surface.actions ?? [] : [];
|
|
1095
1308
|
/**
|
|
1096
1309
|
* 多轮 Agent 循环。
|
|
1097
1310
|
* 工具失败一律转为 ToolResult{ok:false} 回喂 LLM 继续循环;
|
|
@@ -1101,6 +1314,10 @@ var AgentLoop = class {
|
|
|
1101
1314
|
#deps;
|
|
1102
1315
|
#config;
|
|
1103
1316
|
#policy;
|
|
1317
|
+
/** 活跃 run 的 AbortController(cancel(runId) 触发;终态清理) */
|
|
1318
|
+
#controllers = /* @__PURE__ */ new Map();
|
|
1319
|
+
/** 经 cancel() 主动取消的 run(aborted 分支据此区分 cancelled 与 timeout) */
|
|
1320
|
+
#cancelled = /* @__PURE__ */ new Set();
|
|
1104
1321
|
constructor(deps, config = {}) {
|
|
1105
1322
|
this.#deps = {
|
|
1106
1323
|
...deps,
|
|
@@ -1121,6 +1338,18 @@ var AgentLoop = class {
|
|
|
1121
1338
|
interactionTimeoutMs: deps.interaction?.interactionTimeoutMs ?? 3e5
|
|
1122
1339
|
};
|
|
1123
1340
|
}
|
|
1341
|
+
/**
|
|
1342
|
+
* 取消进行中的 run:触发该 run 的 AbortController(与 totalTimeout 硬期限同一通道),
|
|
1343
|
+
* run 以 cancelled(RUN_CANCELLED)终止;未找到(已终态或不属于本实例)返回 false。
|
|
1344
|
+
* 取消在下一个中断点生效(LLM complete/stream 调用;交互等待不强制中断)。
|
|
1345
|
+
*/
|
|
1346
|
+
cancel(runId) {
|
|
1347
|
+
const controller = this.#controllers.get(runId);
|
|
1348
|
+
if (!controller) return false;
|
|
1349
|
+
this.#cancelled.add(runId);
|
|
1350
|
+
controller.abort();
|
|
1351
|
+
return true;
|
|
1352
|
+
}
|
|
1124
1353
|
async run(input) {
|
|
1125
1354
|
const now = this.#deps.clock?.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
1126
1355
|
const runId = input.runId ?? `run-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -1147,15 +1376,21 @@ var AgentLoop = class {
|
|
|
1147
1376
|
toolTimeoutMs: this.#config.toolTimeoutMs,
|
|
1148
1377
|
now,
|
|
1149
1378
|
interactionSeq: 0,
|
|
1379
|
+
surfaceActionSeq: 0,
|
|
1150
1380
|
messages: [],
|
|
1151
1381
|
turn: 0,
|
|
1152
1382
|
renderBlocks: [],
|
|
1383
|
+
surfaceEvents: [],
|
|
1384
|
+
surfacePatchWindowStartedAt: Date.now(),
|
|
1385
|
+
surfacePatchCount: 0,
|
|
1386
|
+
processedSurfaceActionNonces: /* @__PURE__ */ new Set(),
|
|
1153
1387
|
startMs,
|
|
1154
1388
|
pausedMs: 0,
|
|
1155
1389
|
maxTurns: this.#config.maxTurns,
|
|
1156
1390
|
totalTimeoutMs: this.#config.totalTimeoutMs,
|
|
1157
1391
|
controller: new AbortController()
|
|
1158
1392
|
};
|
|
1393
|
+
this.#controllers.set(runId, state.controller);
|
|
1159
1394
|
if (this.#deps.hooks && !this.#deps.hooks.onWarning) this.#deps.hooks.onWarning = (message) => trace.record("run.warning", { message });
|
|
1160
1395
|
const route = this.#deps.catalogFilter ? {
|
|
1161
1396
|
...input.route,
|
|
@@ -1250,7 +1485,10 @@ var AgentLoop = class {
|
|
|
1250
1485
|
signal: state.controller.signal
|
|
1251
1486
|
});
|
|
1252
1487
|
} catch (e) {
|
|
1253
|
-
if (state.controller.signal.aborted)
|
|
1488
|
+
if (state.controller.signal.aborted) {
|
|
1489
|
+
if (this.#cancelled.has(state.runId)) return finish("cancelled", "user-cancelled", "Run cancelled by user", "RUN_CANCELLED");
|
|
1490
|
+
return finish("failed", "timeout", `Agent loop exceeded the total timeout of ${state.totalTimeoutMs}ms`, "RUN_TIMEOUT");
|
|
1491
|
+
}
|
|
1254
1492
|
const code = e instanceof WebSkillError && (e.code === "LLM_UNAVAILABLE" || e.code === "LLM_REQUEST_FAILED") ? e.code : "LLM_REQUEST_FAILED";
|
|
1255
1493
|
return finish("failed", "llm-error", messageOf(e), code);
|
|
1256
1494
|
}
|
|
@@ -1285,6 +1523,7 @@ var AgentLoop = class {
|
|
|
1285
1523
|
toolCallId: call.id,
|
|
1286
1524
|
content: await this.#serializeToolResult(call, result, state)
|
|
1287
1525
|
});
|
|
1526
|
+
await this.#drainSurfaceAction(state);
|
|
1288
1527
|
}
|
|
1289
1528
|
}
|
|
1290
1529
|
} finally {
|
|
@@ -1294,6 +1533,8 @@ var AgentLoop = class {
|
|
|
1294
1533
|
/** 统一终态处理:completed/failed/cancelled;终态即删快照(快照是续命机制,审计归治理) */
|
|
1295
1534
|
async #finish(state, status, reason, output, errorCode) {
|
|
1296
1535
|
this.#disarmDeadline(state);
|
|
1536
|
+
this.#controllers.delete(state.runId);
|
|
1537
|
+
this.#cancelled.delete(state.runId);
|
|
1297
1538
|
const { run, trace } = state;
|
|
1298
1539
|
run.status = status;
|
|
1299
1540
|
run.terminationReason = reason;
|
|
@@ -1338,7 +1579,7 @@ var AgentLoop = class {
|
|
|
1338
1579
|
};
|
|
1339
1580
|
}
|
|
1340
1581
|
/** D3 快照写入(含 pendingInteraction 与过期时间;写失败降级 run.warning) */
|
|
1341
|
-
async #saveSnapshot(state,
|
|
1582
|
+
async #saveSnapshot(state, pending) {
|
|
1342
1583
|
const store = this.#deps.snapshotStore;
|
|
1343
1584
|
if (!store) return;
|
|
1344
1585
|
const snapshot = {
|
|
@@ -1351,10 +1592,14 @@ var AgentLoop = class {
|
|
|
1351
1592
|
turn: state.turn,
|
|
1352
1593
|
activeSkillNames: [...state.activated].sort(),
|
|
1353
1594
|
activatedTools: [...state.activatedTools.values()],
|
|
1354
|
-
pendingInteraction:
|
|
1595
|
+
...pending.interaction ? { pendingInteraction: pending.interaction } : {},
|
|
1596
|
+
...pending.surfaceAction ? { pendingSurfaceAction: pending.surfaceAction } : {},
|
|
1355
1597
|
interactionExpiresAt: state.run.interruptExpiresAt,
|
|
1356
1598
|
renderBlocks: [...state.renderBlocks],
|
|
1599
|
+
surfaceEvents: state.surfaceEvents.map((event) => structuredClone(event)),
|
|
1357
1600
|
interactionSeq: state.interactionSeq,
|
|
1601
|
+
surfaceActionSeq: state.surfaceActionSeq,
|
|
1602
|
+
processedSurfaceActionNonces: [...state.processedSurfaceActionNonces],
|
|
1358
1603
|
pausedMs: state.pausedMs,
|
|
1359
1604
|
config: {
|
|
1360
1605
|
maxTurns: state.maxTurns,
|
|
@@ -1399,25 +1644,35 @@ var AgentLoop = class {
|
|
|
1399
1644
|
toolTimeoutMs: snapshot.config.toolTimeoutMs,
|
|
1400
1645
|
now,
|
|
1401
1646
|
interactionSeq: snapshot.interactionSeq ?? 0,
|
|
1647
|
+
surfaceActionSeq: snapshot.surfaceActionSeq ?? 0,
|
|
1402
1648
|
messages: snapshot.messages.map((m) => ({ ...m })),
|
|
1403
1649
|
turn: snapshot.turn,
|
|
1404
1650
|
renderBlocks: (snapshot.renderBlocks ?? []).map((b) => ({ ...b })),
|
|
1651
|
+
surfaceEvents: (snapshot.surfaceEvents ?? []).map((event) => structuredClone(event)),
|
|
1652
|
+
surfacePatchWindowStartedAt: Date.now(),
|
|
1653
|
+
surfacePatchCount: 0,
|
|
1654
|
+
processedSurfaceActionNonces: new Set(snapshot.processedSurfaceActionNonces ?? []),
|
|
1405
1655
|
startMs,
|
|
1406
1656
|
pausedMs: snapshot.pausedMs ?? 0,
|
|
1407
1657
|
maxTurns: snapshot.config.maxTurns,
|
|
1408
1658
|
totalTimeoutMs: snapshot.config.totalTimeoutMs,
|
|
1409
1659
|
controller: new AbortController()
|
|
1410
1660
|
};
|
|
1661
|
+
this.#controllers.set(runId, state.controller);
|
|
1411
1662
|
if (this.#deps.hooks && !this.#deps.hooks.onWarning) this.#deps.hooks.onWarning = (message) => trace.record("run.warning", { message });
|
|
1663
|
+
const pendingInteraction = snapshot.pendingInteraction;
|
|
1664
|
+
const pendingSurfaceAction = snapshot.pendingSurfaceAction;
|
|
1412
1665
|
trace.record("run.resumed", { data: {
|
|
1413
1666
|
snapshotAt: snapshot.snapshotAt,
|
|
1414
1667
|
turn: snapshot.turn,
|
|
1415
|
-
interactionType:
|
|
1668
|
+
interactionType: pendingSurfaceAction ? "surface-action" : pendingInteraction?.type
|
|
1416
1669
|
} });
|
|
1417
|
-
const pending =
|
|
1670
|
+
const pending = pendingInteraction;
|
|
1418
1671
|
const pendingCall = this.#findPendingToolCall(state.messages);
|
|
1419
1672
|
try {
|
|
1420
|
-
|
|
1673
|
+
await this.#replaySurfaceEvents(state);
|
|
1674
|
+
if (pendingSurfaceAction) await this.#resumeSurfaceAction(state, pendingSurfaceAction);
|
|
1675
|
+
else if ((pending?.type === "form" || pending?.type === "select") && pendingCall) {
|
|
1421
1676
|
const value = await this.#interact(state, pending, {
|
|
1422
1677
|
tool: pendingCall.name,
|
|
1423
1678
|
resumed: true
|
|
@@ -1435,7 +1690,8 @@ var AgentLoop = class {
|
|
|
1435
1690
|
toolCallId: pendingCall.id,
|
|
1436
1691
|
content: await this.#serializeToolResult(pendingCall, result, state)
|
|
1437
1692
|
});
|
|
1438
|
-
|
|
1693
|
+
await this.#drainSurfaceAction(state);
|
|
1694
|
+
} else if (pending?.type === "ask" && pendingCall) {
|
|
1439
1695
|
const value = await this.#interact(state, pending, {
|
|
1440
1696
|
tool: pendingCall.name,
|
|
1441
1697
|
resumed: true
|
|
@@ -1456,6 +1712,7 @@ var AgentLoop = class {
|
|
|
1456
1712
|
toolCallId: pendingCall.id,
|
|
1457
1713
|
content: await this.#serializeToolResult(pendingCall, result, state)
|
|
1458
1714
|
});
|
|
1715
|
+
await this.#drainSurfaceAction(state);
|
|
1459
1716
|
} else if (pendingCall) {
|
|
1460
1717
|
const result = await this.#executeCall(pendingCall, state);
|
|
1461
1718
|
state.messages.push({
|
|
@@ -1463,6 +1720,7 @@ var AgentLoop = class {
|
|
|
1463
1720
|
toolCallId: pendingCall.id,
|
|
1464
1721
|
content: await this.#serializeToolResult(pendingCall, result, state)
|
|
1465
1722
|
});
|
|
1723
|
+
await this.#drainSurfaceAction(state);
|
|
1466
1724
|
}
|
|
1467
1725
|
for (;;) {
|
|
1468
1726
|
const next = this.#findPendingToolCall(state.messages);
|
|
@@ -1473,6 +1731,7 @@ var AgentLoop = class {
|
|
|
1473
1731
|
toolCallId: next.id,
|
|
1474
1732
|
content: await this.#serializeToolResult(next, result, state)
|
|
1475
1733
|
});
|
|
1734
|
+
await this.#drainSurfaceAction(state);
|
|
1476
1735
|
}
|
|
1477
1736
|
} catch (e) {
|
|
1478
1737
|
if (e instanceof RunTerminated) return this.#finish(state, e.outcome.status, e.outcome.reason, e.outcome.message, e.outcome.code);
|
|
@@ -1569,7 +1828,7 @@ var AgentLoop = class {
|
|
|
1569
1828
|
const { run } = state;
|
|
1570
1829
|
run.status = "interrupted";
|
|
1571
1830
|
run.interruptExpiresAt = new Date(Date.parse(state.now()) + this.#policy.interactionTimeoutMs).toISOString();
|
|
1572
|
-
await this.#saveSnapshot(state, request);
|
|
1831
|
+
await this.#saveSnapshot(state, { interaction: request });
|
|
1573
1832
|
await this.#lifecycle("interact", state, {
|
|
1574
1833
|
interactionId: request.id,
|
|
1575
1834
|
type: request.type
|
|
@@ -1628,10 +1887,14 @@ var AgentLoop = class {
|
|
|
1628
1887
|
});
|
|
1629
1888
|
}
|
|
1630
1889
|
async #executeCall(call, state) {
|
|
1890
|
+
const argsSummary = summarizeArgs(call.arguments);
|
|
1891
|
+
const callStartMs = Date.parse(state.now());
|
|
1631
1892
|
state.trace.record("tool.started", { data: {
|
|
1632
1893
|
name: call.name,
|
|
1633
|
-
callId: call.id
|
|
1894
|
+
callId: call.id,
|
|
1895
|
+
args: argsSummary
|
|
1634
1896
|
} });
|
|
1897
|
+
this.#emitTool(state, "started", call);
|
|
1635
1898
|
let result;
|
|
1636
1899
|
if (call.name === "read_skill_file") result = await this.#handleReadSkillFile(call, state);
|
|
1637
1900
|
else if (call.name === "ask_user") result = await this.#handleAskUser(call, state);
|
|
@@ -1643,11 +1906,15 @@ var AgentLoop = class {
|
|
|
1643
1906
|
result = source ? await source.call(call.name, call.arguments) : toolError(resolution.code, resolution.message);
|
|
1644
1907
|
}
|
|
1645
1908
|
}
|
|
1909
|
+
const durationMs = Date.parse(state.now()) - callStartMs;
|
|
1646
1910
|
if (result.ok) {
|
|
1647
1911
|
state.trace.record("tool.completed", { data: {
|
|
1648
1912
|
name: call.name,
|
|
1649
|
-
callId: call.id
|
|
1913
|
+
callId: call.id,
|
|
1914
|
+
args: argsSummary,
|
|
1915
|
+
durationMs
|
|
1650
1916
|
} });
|
|
1917
|
+
this.#emitTool(state, "completed", call);
|
|
1651
1918
|
for (const item of result.content) {
|
|
1652
1919
|
if (item.type !== "json") continue;
|
|
1653
1920
|
const chart = extractChartSpec(item.data);
|
|
@@ -1655,21 +1922,199 @@ var AgentLoop = class {
|
|
|
1655
1922
|
type: "chart",
|
|
1656
1923
|
chart
|
|
1657
1924
|
});
|
|
1925
|
+
try {
|
|
1926
|
+
for (const event of extractUiSurfaceEvents(item.data)) await this.#renderSurface(state, event);
|
|
1927
|
+
} catch (e) {
|
|
1928
|
+
state.trace.record("run.warning", {
|
|
1929
|
+
message: `UI surface rejected: ${messageOf(e)}`,
|
|
1930
|
+
data: e instanceof WebSkillError ? { code: e.code } : void 0
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1658
1933
|
}
|
|
1659
|
-
} else
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1934
|
+
} else {
|
|
1935
|
+
state.trace.record("tool.failed", {
|
|
1936
|
+
message: result.error?.message,
|
|
1937
|
+
data: {
|
|
1938
|
+
name: call.name,
|
|
1939
|
+
callId: call.id,
|
|
1940
|
+
code: result.error?.code,
|
|
1941
|
+
args: argsSummary,
|
|
1942
|
+
durationMs
|
|
1943
|
+
}
|
|
1944
|
+
});
|
|
1945
|
+
this.#emitTool(state, "failed", call);
|
|
1946
|
+
}
|
|
1667
1947
|
for (const artifact of result.artifacts ?? []) state.trace.record("artifact.created", { data: {
|
|
1668
1948
|
artifactId: artifact.id,
|
|
1669
1949
|
path: artifact.path
|
|
1670
1950
|
} });
|
|
1671
1951
|
return result;
|
|
1672
1952
|
}
|
|
1953
|
+
/** Attaches trusted run provenance, then records only events accepted by the configured bridge. */
|
|
1954
|
+
async #renderSurface(state, event) {
|
|
1955
|
+
const bridge = this.#deps.uiBridge;
|
|
1956
|
+
if (!bridge?.renderSurface) {
|
|
1957
|
+
state.trace.record("run.warning", { message: "UiBridge does not support renderSurface; UI surface was not rendered" });
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1960
|
+
const attributed = this.#attributeSurfaceEvent(state, event);
|
|
1961
|
+
if (attributed.type === "patch") this.#consumeSurfacePatchBudget(state);
|
|
1962
|
+
await bridge.renderSurface(attributed);
|
|
1963
|
+
state.surfaceEvents.push(structuredClone(attributed));
|
|
1964
|
+
if (attributed.type === "open") {
|
|
1965
|
+
const waiting = surfaceActions(attributed.surface).filter((action) => action.awaitResponse);
|
|
1966
|
+
if (waiting.length > 1) throw new WebSkillError("VALIDATION_FAILED", "A UI surface can wait for only one action");
|
|
1967
|
+
const action = waiting[0];
|
|
1968
|
+
if (action?.nonce) if (!bridge.requestSurfaceAction) state.trace.record("run.warning", { message: "UiBridge does not support requestSurfaceAction; UI surface action will not pause the run" });
|
|
1969
|
+
else if (state.pendingSurfaceAction) throw new WebSkillError("VALIDATION_FAILED", "Only one UI surface action can be pending at a time");
|
|
1970
|
+
else state.pendingSurfaceAction = {
|
|
1971
|
+
runId: state.runId,
|
|
1972
|
+
surfaceId: attributed.surface.id,
|
|
1973
|
+
actionId: action.id,
|
|
1974
|
+
intent: action.intent,
|
|
1975
|
+
nonce: action.nonce
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
#consumeSurfacePatchBudget(state) {
|
|
1980
|
+
const now = Date.now();
|
|
1981
|
+
if (now - state.surfacePatchWindowStartedAt >= 1e3) {
|
|
1982
|
+
state.surfacePatchWindowStartedAt = now;
|
|
1983
|
+
state.surfacePatchCount = 0;
|
|
1984
|
+
}
|
|
1985
|
+
state.surfacePatchCount += 1;
|
|
1986
|
+
if (state.surfacePatchCount > MAX_SURFACE_PATCHES_PER_SECOND) throw new WebSkillError("VALIDATION_FAILED", `UI surface patch rate exceeds ${MAX_SURFACE_PATCHES_PER_SECOND} events per second`);
|
|
1987
|
+
}
|
|
1988
|
+
/** Assigns unforgeable action nonces after model output has passed structural validation. */
|
|
1989
|
+
#attributeSurfaceEvent(state, event) {
|
|
1990
|
+
const actions = event.type === "open" ? surfaceActions(event.surface) : [];
|
|
1991
|
+
if (event.type !== "open" || actions.length === 0) return {
|
|
1992
|
+
...event,
|
|
1993
|
+
runId: state.runId
|
|
1994
|
+
};
|
|
1995
|
+
return {
|
|
1996
|
+
type: "open",
|
|
1997
|
+
runId: state.runId,
|
|
1998
|
+
surface: {
|
|
1999
|
+
...event.surface,
|
|
2000
|
+
actions: actions.map((action) => ({
|
|
2001
|
+
...action,
|
|
2002
|
+
nonce: `surface-${state.runId}-${++state.surfaceActionSeq}`
|
|
2003
|
+
}))
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
/** Awaits the single action emitted with the most recently persisted tool result. */
|
|
2008
|
+
async #drainSurfaceAction(state) {
|
|
2009
|
+
const request = state.pendingSurfaceAction;
|
|
2010
|
+
if (!request) return;
|
|
2011
|
+
state.pendingSurfaceAction = void 0;
|
|
2012
|
+
await this.#acceptSurfaceAction(state, request);
|
|
2013
|
+
}
|
|
2014
|
+
/** Restores a checkpointed action wait without re-running the tool that emitted its surface. */
|
|
2015
|
+
async #resumeSurfaceAction(state, request) {
|
|
2016
|
+
await this.#acceptSurfaceAction(state, request, true);
|
|
2017
|
+
}
|
|
2018
|
+
async #acceptSurfaceAction(state, request, resumed = false) {
|
|
2019
|
+
const response = await this.#interactSurfaceAction(state, request, resumed);
|
|
2020
|
+
if (response.cancelled) throw new RunTerminated({
|
|
2021
|
+
status: "cancelled",
|
|
2022
|
+
reason: "user-cancelled",
|
|
2023
|
+
message: "Surface action cancelled by user",
|
|
2024
|
+
code: "RUN_CANCELLED"
|
|
2025
|
+
});
|
|
2026
|
+
if (response.runId !== request.runId || response.surfaceId !== request.surfaceId || response.actionId !== request.actionId || response.nonce !== request.nonce) throw new BridgeRequestError("UiBridge returned a surface action response with an invalid capability nonce");
|
|
2027
|
+
if (state.processedSurfaceActionNonces.has(response.nonce)) throw new BridgeRequestError("UiBridge returned a surface action response with an already consumed capability nonce");
|
|
2028
|
+
state.processedSurfaceActionNonces.add(response.nonce);
|
|
2029
|
+
state.messages.push({
|
|
2030
|
+
role: "user",
|
|
2031
|
+
content: JSON.stringify({
|
|
2032
|
+
type: "webskill_surface_action",
|
|
2033
|
+
surfaceId: response.surfaceId,
|
|
2034
|
+
actionId: response.actionId,
|
|
2035
|
+
intent: response.intent,
|
|
2036
|
+
value: response.value ?? null
|
|
2037
|
+
})
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
async #interactSurfaceAction(state, request, resumed) {
|
|
2041
|
+
const bridge = this.#deps.uiBridge;
|
|
2042
|
+
if (!bridge?.requestSurfaceAction) throw new BridgeRequestError("UiBridge does not support requestSurfaceAction");
|
|
2043
|
+
this.#disarmDeadline(state);
|
|
2044
|
+
const waitStartMs = Date.parse(state.now());
|
|
2045
|
+
try {
|
|
2046
|
+
const { run } = state;
|
|
2047
|
+
run.status = "interrupted";
|
|
2048
|
+
run.interruptExpiresAt = new Date(Date.parse(state.now()) + this.#policy.interactionTimeoutMs).toISOString();
|
|
2049
|
+
await this.#saveSnapshot(state, { surfaceAction: request });
|
|
2050
|
+
await this.#lifecycle("interact", state, {
|
|
2051
|
+
surfaceId: request.surfaceId,
|
|
2052
|
+
actionId: request.actionId,
|
|
2053
|
+
nonce: request.nonce,
|
|
2054
|
+
...resumed ? { resumed: true } : {}
|
|
2055
|
+
});
|
|
2056
|
+
state.trace.record("ui.surface-action.requested", { data: {
|
|
2057
|
+
surfaceId: request.surfaceId,
|
|
2058
|
+
actionId: request.actionId,
|
|
2059
|
+
nonce: request.nonce,
|
|
2060
|
+
...resumed ? { resumed: true } : {}
|
|
2061
|
+
} });
|
|
2062
|
+
const response = await this.#withInteractionTimeout(bridge.requestSurfaceAction(request), this.#policy.interactionTimeoutMs, () => bridge.cancelSurfaceAction?.(request.nonce));
|
|
2063
|
+
run.status = "running";
|
|
2064
|
+
run.interruptExpiresAt = void 0;
|
|
2065
|
+
state.trace.record("ui.surface-action.resolved", { data: {
|
|
2066
|
+
surfaceId: request.surfaceId,
|
|
2067
|
+
actionId: request.actionId,
|
|
2068
|
+
nonce: request.nonce
|
|
2069
|
+
} });
|
|
2070
|
+
await this.#lifecycle("execute", state, { surfaceAction: request.actionId });
|
|
2071
|
+
return response;
|
|
2072
|
+
} catch (e) {
|
|
2073
|
+
state.run.status = "running";
|
|
2074
|
+
state.run.interruptExpiresAt = void 0;
|
|
2075
|
+
if (e instanceof WebSkillError && e.code === "RUN_INTERACTION_TIMEOUT") throw new RunTerminated({
|
|
2076
|
+
status: "failed",
|
|
2077
|
+
reason: "interaction-timeout",
|
|
2078
|
+
message: e.message,
|
|
2079
|
+
code: "RUN_INTERACTION_TIMEOUT"
|
|
2080
|
+
});
|
|
2081
|
+
if (e instanceof RunTerminated || e instanceof BridgeRequestError) throw e;
|
|
2082
|
+
throw new BridgeRequestError(messageOf(e));
|
|
2083
|
+
} finally {
|
|
2084
|
+
state.pausedMs += Date.parse(state.now()) - waitStartMs;
|
|
2085
|
+
this.#armDeadline(state);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
/** Replays the persisted stream before an interrupted interaction is rendered again. */
|
|
2089
|
+
async #replaySurfaceEvents(state) {
|
|
2090
|
+
const bridge = this.#deps.uiBridge;
|
|
2091
|
+
if (!bridge?.renderSurface || state.surfaceEvents.length === 0) return;
|
|
2092
|
+
try {
|
|
2093
|
+
for (const event of state.surfaceEvents) await bridge.renderSurface(structuredClone(event));
|
|
2094
|
+
} catch (e) {
|
|
2095
|
+
state.trace.record("run.warning", { message: `Failed to replay UI surfaces: ${messageOf(e)}` });
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* 逐工具实时事件(execute 相位,data.type='tool'):chatbot 思维链工具行等的 live 状态源;
|
|
2100
|
+
* 与既有 execute 相位事件({turn})并存,监听方按 data.type 区分。
|
|
2101
|
+
* data.args 为参数摘要(JSON 截断 100 字符,展开详情用)。
|
|
2102
|
+
*/
|
|
2103
|
+
#emitTool(state, status, call) {
|
|
2104
|
+
this.#deps.eventBus?.emit({
|
|
2105
|
+
phase: "execute",
|
|
2106
|
+
runId: state.runId,
|
|
2107
|
+
sessionId: state.run.sessionId,
|
|
2108
|
+
ts: state.now(),
|
|
2109
|
+
data: {
|
|
2110
|
+
type: "tool",
|
|
2111
|
+
status,
|
|
2112
|
+
name: call.name,
|
|
2113
|
+
callId: call.id,
|
|
2114
|
+
args: summarizeArgs(call.arguments)
|
|
2115
|
+
}
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
1673
2118
|
async #handleAskUser(call, state) {
|
|
1674
2119
|
const question = call.arguments["question"];
|
|
1675
2120
|
if (typeof question !== "string" || question === "") return toolError("TOOL_EXECUTION_FAILED", "ask_user requires a non-empty \"question\" string argument");
|
|
@@ -2140,6 +2585,8 @@ var WebSkillRuntime = class {
|
|
|
2140
2585
|
#session;
|
|
2141
2586
|
#events;
|
|
2142
2587
|
#catalogCache;
|
|
2588
|
+
/** 活跃 run 的 loop 实例(cancel(runId) 路由;终态清理) */
|
|
2589
|
+
#loops = /* @__PURE__ */ new Map();
|
|
2143
2590
|
constructor(deps) {
|
|
2144
2591
|
this.#deps = {
|
|
2145
2592
|
...deps,
|
|
@@ -2224,7 +2671,7 @@ var WebSkillRuntime = class {
|
|
|
2224
2671
|
const catalog = providerEntries.length > 0 ? { entries: mergeCatalogEntries(cache.catalog.entries, providerEntries) } : cache.catalog;
|
|
2225
2672
|
const filteredCatalog = this.#deps.catalogFilter ? { entries: await this.#deps.catalogFilter(catalog.entries) } : catalog;
|
|
2226
2673
|
const route = await this.#router.route(filteredCatalog);
|
|
2227
|
-
const
|
|
2674
|
+
const loop = new AgentLoop({
|
|
2228
2675
|
llm: this.#deps.llm,
|
|
2229
2676
|
executor: this.#deps.executor,
|
|
2230
2677
|
schemaInferer: this.#deps.schemaInferer,
|
|
@@ -2243,12 +2690,21 @@ var WebSkillRuntime = class {
|
|
|
2243
2690
|
catalogFilter: this.#deps.catalogFilter,
|
|
2244
2691
|
snapshotStore: this.#deps.snapshotStore,
|
|
2245
2692
|
skillStateGuard: this.#deps.skillStateGuard
|
|
2246
|
-
}, this.#deps.config)
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2693
|
+
}, this.#deps.config);
|
|
2694
|
+
const runId = `run-${Math.random().toString(36).slice(2, 10)}`;
|
|
2695
|
+
this.#loops.set(runId, loop);
|
|
2696
|
+
let result;
|
|
2697
|
+
try {
|
|
2698
|
+
result = await loop.run({
|
|
2699
|
+
sessionId: options.sessionId ?? this.#session.id,
|
|
2700
|
+
userPrompt,
|
|
2701
|
+
route,
|
|
2702
|
+
runId,
|
|
2703
|
+
...options.history ? { history: options.history } : {}
|
|
2704
|
+
});
|
|
2705
|
+
} finally {
|
|
2706
|
+
this.#loops.delete(runId);
|
|
2707
|
+
}
|
|
2252
2708
|
for (const failure of providerFailures) result.run.trace.push({
|
|
2253
2709
|
id: `evt-provider-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
2254
2710
|
runId: result.run.id,
|
|
@@ -2323,7 +2779,7 @@ var WebSkillRuntime = class {
|
|
|
2323
2779
|
if (!this.#catalogCache) await this.discover();
|
|
2324
2780
|
const cache = this.#catalogCache;
|
|
2325
2781
|
if (!cache) throw new WebSkillError("RUN_FAILED", "discover() did not populate the catalog cache");
|
|
2326
|
-
|
|
2782
|
+
const loop = new AgentLoop({
|
|
2327
2783
|
llm: this.#deps.llm,
|
|
2328
2784
|
executor: this.#deps.executor,
|
|
2329
2785
|
schemaInferer: this.#deps.schemaInferer,
|
|
@@ -2342,7 +2798,21 @@ var WebSkillRuntime = class {
|
|
|
2342
2798
|
catalogFilter: this.#deps.catalogFilter,
|
|
2343
2799
|
snapshotStore: store,
|
|
2344
2800
|
skillStateGuard: this.#deps.skillStateGuard
|
|
2345
|
-
}, this.#deps.config)
|
|
2801
|
+
}, this.#deps.config);
|
|
2802
|
+
this.#loops.set(runId, loop);
|
|
2803
|
+
try {
|
|
2804
|
+
return await loop.resume(snapshot);
|
|
2805
|
+
} finally {
|
|
2806
|
+
this.#loops.delete(runId);
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
/**
|
|
2810
|
+
* 取消进行中的 run(chatbot Stop 按钮等):触发该 run 的 AbortController,
|
|
2811
|
+
* run 以 cancelled(RUN_CANCELLED)终止;未找到活跃 run 返回 false。
|
|
2812
|
+
* 取消在下一个中断点生效(LLM complete/stream;交互等待不强制中断)。
|
|
2813
|
+
*/
|
|
2814
|
+
cancel(runId) {
|
|
2815
|
+
return this.#loops.get(runId)?.cancel(runId) ?? false;
|
|
2346
2816
|
}
|
|
2347
2817
|
};
|
|
2348
2818
|
/**
|
|
@@ -2813,4 +3283,4 @@ var CapabilityApproval = class CapabilityApproval {
|
|
|
2813
3283
|
};
|
|
2814
3284
|
|
|
2815
3285
|
//#endregion
|
|
2816
|
-
export {
|
|
3286
|
+
export { fromVercelStreamPart as A, toLlmToolSpec as B, bridgeError as C, extractChartSpec as D, createWebSkillApi as E, normalizeToolContent as F, validateUiSurface as H, normalizeToolError as I, parseBridgeRequest as L, mergeCatalogEntries as M, networkUrlHost as N, extractUiSurfaceEvents as O, normalizeErrorCode as P, resolveToolName as R, WebSkillRuntime as S, createScriptContext as T, validateUiSurfaceEvent as U, toVercelToolSpecs as V, READ_SKILL_FILE_TOOL as _, AnthropicClient as a, SerializingMemoryStore as b, FsArtifactStore as c, FullDisclosureRouter as d, GoogleGenAiClient as f, READ_SKILL_FILE_INPUT_SCHEMA as g, ProgressiveRouter as h, AgentLoop as i, isNetworkAllowed as j, fromVercelResult as k, FsMemoryStore as l, OpenAiCompatibleClient as m, ASK_USER_TOOL as n, CapabilityApproval as o, HookRunner as p, ASK_USER_TOOL_NAME as r, EventBus as s, ASK_USER_INPUT_SCHEMA as t, FsRunSnapshotStore as u, READ_SKILL_FILE_TOOL_NAME as v, buildRenderResult as w, TraceRecorder as x, RUN_SNAPSHOT_SCHEMA_VERSION as y, schemaToForm as z };
|