@webskill/sdk 0.2.6 → 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-BXpDDZpR.js → dist-BdOW8N4V.js} +398 -14
- package/dist/{dist-DWFDb1Ww.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 +3 -3
- package/dist/governance.js +2 -2
- package/dist/{index-DRlYzdr2.d.ts → index-BHL5FWGw.d.ts} +2 -2
- package/dist/index-BNQNSDKg.d.ts +580 -0
- package/dist/{index-DJOha4b6.d.ts → index-BpIK7tJM.d.ts} +23 -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
|
@@ -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;
|
|
@@ -1097,6 +1304,7 @@ const summarizeArgs = (args) => {
|
|
|
1097
1304
|
const json = JSON.stringify(args);
|
|
1098
1305
|
return json.length > 100 ? `${json.slice(0, 100)}…` : json;
|
|
1099
1306
|
};
|
|
1307
|
+
const surfaceActions = (surface) => "actions" in surface ? surface.actions ?? [] : [];
|
|
1100
1308
|
/**
|
|
1101
1309
|
* 多轮 Agent 循环。
|
|
1102
1310
|
* 工具失败一律转为 ToolResult{ok:false} 回喂 LLM 继续循环;
|
|
@@ -1168,9 +1376,14 @@ var AgentLoop = class {
|
|
|
1168
1376
|
toolTimeoutMs: this.#config.toolTimeoutMs,
|
|
1169
1377
|
now,
|
|
1170
1378
|
interactionSeq: 0,
|
|
1379
|
+
surfaceActionSeq: 0,
|
|
1171
1380
|
messages: [],
|
|
1172
1381
|
turn: 0,
|
|
1173
1382
|
renderBlocks: [],
|
|
1383
|
+
surfaceEvents: [],
|
|
1384
|
+
surfacePatchWindowStartedAt: Date.now(),
|
|
1385
|
+
surfacePatchCount: 0,
|
|
1386
|
+
processedSurfaceActionNonces: /* @__PURE__ */ new Set(),
|
|
1174
1387
|
startMs,
|
|
1175
1388
|
pausedMs: 0,
|
|
1176
1389
|
maxTurns: this.#config.maxTurns,
|
|
@@ -1310,6 +1523,7 @@ var AgentLoop = class {
|
|
|
1310
1523
|
toolCallId: call.id,
|
|
1311
1524
|
content: await this.#serializeToolResult(call, result, state)
|
|
1312
1525
|
});
|
|
1526
|
+
await this.#drainSurfaceAction(state);
|
|
1313
1527
|
}
|
|
1314
1528
|
}
|
|
1315
1529
|
} finally {
|
|
@@ -1365,7 +1579,7 @@ var AgentLoop = class {
|
|
|
1365
1579
|
};
|
|
1366
1580
|
}
|
|
1367
1581
|
/** D3 快照写入(含 pendingInteraction 与过期时间;写失败降级 run.warning) */
|
|
1368
|
-
async #saveSnapshot(state,
|
|
1582
|
+
async #saveSnapshot(state, pending) {
|
|
1369
1583
|
const store = this.#deps.snapshotStore;
|
|
1370
1584
|
if (!store) return;
|
|
1371
1585
|
const snapshot = {
|
|
@@ -1378,10 +1592,14 @@ var AgentLoop = class {
|
|
|
1378
1592
|
turn: state.turn,
|
|
1379
1593
|
activeSkillNames: [...state.activated].sort(),
|
|
1380
1594
|
activatedTools: [...state.activatedTools.values()],
|
|
1381
|
-
pendingInteraction:
|
|
1595
|
+
...pending.interaction ? { pendingInteraction: pending.interaction } : {},
|
|
1596
|
+
...pending.surfaceAction ? { pendingSurfaceAction: pending.surfaceAction } : {},
|
|
1382
1597
|
interactionExpiresAt: state.run.interruptExpiresAt,
|
|
1383
1598
|
renderBlocks: [...state.renderBlocks],
|
|
1599
|
+
surfaceEvents: state.surfaceEvents.map((event) => structuredClone(event)),
|
|
1384
1600
|
interactionSeq: state.interactionSeq,
|
|
1601
|
+
surfaceActionSeq: state.surfaceActionSeq,
|
|
1602
|
+
processedSurfaceActionNonces: [...state.processedSurfaceActionNonces],
|
|
1385
1603
|
pausedMs: state.pausedMs,
|
|
1386
1604
|
config: {
|
|
1387
1605
|
maxTurns: state.maxTurns,
|
|
@@ -1426,9 +1644,14 @@ var AgentLoop = class {
|
|
|
1426
1644
|
toolTimeoutMs: snapshot.config.toolTimeoutMs,
|
|
1427
1645
|
now,
|
|
1428
1646
|
interactionSeq: snapshot.interactionSeq ?? 0,
|
|
1647
|
+
surfaceActionSeq: snapshot.surfaceActionSeq ?? 0,
|
|
1429
1648
|
messages: snapshot.messages.map((m) => ({ ...m })),
|
|
1430
1649
|
turn: snapshot.turn,
|
|
1431
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 ?? []),
|
|
1432
1655
|
startMs,
|
|
1433
1656
|
pausedMs: snapshot.pausedMs ?? 0,
|
|
1434
1657
|
maxTurns: snapshot.config.maxTurns,
|
|
@@ -1437,15 +1660,19 @@ var AgentLoop = class {
|
|
|
1437
1660
|
};
|
|
1438
1661
|
this.#controllers.set(runId, state.controller);
|
|
1439
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;
|
|
1440
1665
|
trace.record("run.resumed", { data: {
|
|
1441
1666
|
snapshotAt: snapshot.snapshotAt,
|
|
1442
1667
|
turn: snapshot.turn,
|
|
1443
|
-
interactionType:
|
|
1668
|
+
interactionType: pendingSurfaceAction ? "surface-action" : pendingInteraction?.type
|
|
1444
1669
|
} });
|
|
1445
|
-
const pending =
|
|
1670
|
+
const pending = pendingInteraction;
|
|
1446
1671
|
const pendingCall = this.#findPendingToolCall(state.messages);
|
|
1447
1672
|
try {
|
|
1448
|
-
|
|
1673
|
+
await this.#replaySurfaceEvents(state);
|
|
1674
|
+
if (pendingSurfaceAction) await this.#resumeSurfaceAction(state, pendingSurfaceAction);
|
|
1675
|
+
else if ((pending?.type === "form" || pending?.type === "select") && pendingCall) {
|
|
1449
1676
|
const value = await this.#interact(state, pending, {
|
|
1450
1677
|
tool: pendingCall.name,
|
|
1451
1678
|
resumed: true
|
|
@@ -1463,7 +1690,8 @@ var AgentLoop = class {
|
|
|
1463
1690
|
toolCallId: pendingCall.id,
|
|
1464
1691
|
content: await this.#serializeToolResult(pendingCall, result, state)
|
|
1465
1692
|
});
|
|
1466
|
-
|
|
1693
|
+
await this.#drainSurfaceAction(state);
|
|
1694
|
+
} else if (pending?.type === "ask" && pendingCall) {
|
|
1467
1695
|
const value = await this.#interact(state, pending, {
|
|
1468
1696
|
tool: pendingCall.name,
|
|
1469
1697
|
resumed: true
|
|
@@ -1484,6 +1712,7 @@ var AgentLoop = class {
|
|
|
1484
1712
|
toolCallId: pendingCall.id,
|
|
1485
1713
|
content: await this.#serializeToolResult(pendingCall, result, state)
|
|
1486
1714
|
});
|
|
1715
|
+
await this.#drainSurfaceAction(state);
|
|
1487
1716
|
} else if (pendingCall) {
|
|
1488
1717
|
const result = await this.#executeCall(pendingCall, state);
|
|
1489
1718
|
state.messages.push({
|
|
@@ -1491,6 +1720,7 @@ var AgentLoop = class {
|
|
|
1491
1720
|
toolCallId: pendingCall.id,
|
|
1492
1721
|
content: await this.#serializeToolResult(pendingCall, result, state)
|
|
1493
1722
|
});
|
|
1723
|
+
await this.#drainSurfaceAction(state);
|
|
1494
1724
|
}
|
|
1495
1725
|
for (;;) {
|
|
1496
1726
|
const next = this.#findPendingToolCall(state.messages);
|
|
@@ -1501,6 +1731,7 @@ var AgentLoop = class {
|
|
|
1501
1731
|
toolCallId: next.id,
|
|
1502
1732
|
content: await this.#serializeToolResult(next, result, state)
|
|
1503
1733
|
});
|
|
1734
|
+
await this.#drainSurfaceAction(state);
|
|
1504
1735
|
}
|
|
1505
1736
|
} catch (e) {
|
|
1506
1737
|
if (e instanceof RunTerminated) return this.#finish(state, e.outcome.status, e.outcome.reason, e.outcome.message, e.outcome.code);
|
|
@@ -1597,7 +1828,7 @@ var AgentLoop = class {
|
|
|
1597
1828
|
const { run } = state;
|
|
1598
1829
|
run.status = "interrupted";
|
|
1599
1830
|
run.interruptExpiresAt = new Date(Date.parse(state.now()) + this.#policy.interactionTimeoutMs).toISOString();
|
|
1600
|
-
await this.#saveSnapshot(state, request);
|
|
1831
|
+
await this.#saveSnapshot(state, { interaction: request });
|
|
1601
1832
|
await this.#lifecycle("interact", state, {
|
|
1602
1833
|
interactionId: request.id,
|
|
1603
1834
|
type: request.type
|
|
@@ -1691,6 +1922,14 @@ var AgentLoop = class {
|
|
|
1691
1922
|
type: "chart",
|
|
1692
1923
|
chart
|
|
1693
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
|
+
}
|
|
1694
1933
|
}
|
|
1695
1934
|
} else {
|
|
1696
1935
|
state.trace.record("tool.failed", {
|
|
@@ -1711,10 +1950,155 @@ var AgentLoop = class {
|
|
|
1711
1950
|
} });
|
|
1712
1951
|
return result;
|
|
1713
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
|
+
}
|
|
1714
2098
|
/**
|
|
1715
|
-
* 逐工具实时事件(execute 相位,data.type='tool'):chatbot
|
|
2099
|
+
* 逐工具实时事件(execute 相位,data.type='tool'):chatbot 思维链工具行等的 live 状态源;
|
|
1716
2100
|
* 与既有 execute 相位事件({turn})并存,监听方按 data.type 区分。
|
|
1717
|
-
* data.args 为参数摘要(JSON 截断 100
|
|
2101
|
+
* data.args 为参数摘要(JSON 截断 100 字符,展开详情用)。
|
|
1718
2102
|
*/
|
|
1719
2103
|
#emitTool(state, status, call) {
|
|
1720
2104
|
this.#deps.eventBus?.emit({
|
|
@@ -2899,4 +3283,4 @@ var CapabilityApproval = class CapabilityApproval {
|
|
|
2899
3283
|
};
|
|
2900
3284
|
|
|
2901
3285
|
//#endregion
|
|
2902
|
-
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 };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { A as resolveArchiveLimits, C as messageOf, D as readResponseWithLimit, E as parseSkillPackManifest, M as unzipWithLimits, N as validateSkills, P as verifyManifest, T as parseSkillMarkdown, b as exportSkills, d as assertRemoteUrlAllowed, h as buildManifest, i as SKILL_MANIFEST_FILE, j as resolveInsideRoot, p as atomicWriteText, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, u as WebSkillError, x as isValidSkillName } from "./dist-BQzncxXg.js";
|
|
2
|
-
import {
|
|
2
|
+
import { C as bridgeError, F as normalizeToolContent, I as normalizeToolError, L as parseBridgeRequest, N as networkUrlHost, c as FsArtifactStore, j as isNetworkAllowed, l as FsMemoryStore, o as CapabilityApproval } from "./dist-BdOW8N4V.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { unzipSync, zipSync } from "fflate";
|
|
5
5
|
import { existsSync, promises, realpathSync } from "node:fs";
|