@webskill/sdk 0.6.0 → 0.7.0
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/agent.d.ts +2 -2
- package/dist/agent.js +217 -27
- package/dist/browser.d.ts +30 -4
- package/dist/browser.js +196 -12
- package/dist/{catalogComponents-Dr5dFMAb-Dacibl1e.js → catalogComponents-Dr5dFMAb-DKH_7VPI.js} +723 -931
- package/dist/{dist-DusANsrn.js → dist-D0qW6e40.js} +246 -39
- package/dist/eventTypes-DjIQpt8Y-Bj3vghj4.js +32 -0
- package/dist/governance.d.ts +78 -11
- package/dist/governance.js +172 -49
- package/dist/{index-BuTpBMzr.d.ts → index-Ba3xFtfz.d.ts} +156 -5
- package/dist/{index-BMocOEi0.d.ts → index-BwsK9lGk.d.ts} +2 -2
- package/dist/{index-C-KFAZoF.d.ts → index-DkbABR43.d.ts} +40 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/mcp.d.ts +123 -6
- package/dist/mcp.js +224 -27
- package/dist/node.d.ts +3 -3
- package/dist/node.js +3 -2
- package/dist/{openUiLibrary-Bdrji9qK-DzAxRlTY.js → openUiLibrary-Bdrji9qK-D2LxmM-a.js} +1 -1
- package/dist/{skillVersionStore-BzLbzFOL-CxdAFWO2.d.ts → skillVersionStore-Bl-ElD45-CWPvGvoq.d.ts} +8 -2
- package/dist/testing.d.ts +1 -1
- package/dist/{types-4pg-qp_I-Gq63X8Oa.d.ts → types-CcxRLdJG-DCXyw1US.d.ts} +31 -4
- package/dist/ui-react.d.ts +5 -3
- package/dist/ui-react.js +9 -27
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +1 -1
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ const DEFS_KEY = "$defs";
|
|
|
12
12
|
const SELF_REF = "#";
|
|
13
13
|
const NODE_NAME = "Node";
|
|
14
14
|
const COMPONENT_PREFIX = "Component_";
|
|
15
|
-
/** 值为「名称 → 子 schema
|
|
15
|
+
/** 值为「名称 → 子 schema」的映射。包内导出:vendorSchema 要用同一套下钻规则(两套会漂移)。 */
|
|
16
16
|
const SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
17
17
|
"properties",
|
|
18
18
|
"patternProperties",
|
|
@@ -41,11 +41,11 @@ const SCHEMA_KEYS = /* @__PURE__ */ new Set([
|
|
|
41
41
|
"then",
|
|
42
42
|
"else"
|
|
43
43
|
]);
|
|
44
|
-
function isRecord$
|
|
44
|
+
function isRecord$6(value) {
|
|
45
45
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
46
46
|
}
|
|
47
47
|
function isSelfRef(value) {
|
|
48
|
-
return isRecord$
|
|
48
|
+
return isRecord$6(value) && value["$ref"] === SELF_REF;
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* 按 JSON Schema 关键字下降到子 schema。
|
|
@@ -55,7 +55,7 @@ function isSelfRef(value) {
|
|
|
55
55
|
function forEachSubSchema(node, visit) {
|
|
56
56
|
for (const [key, value] of Object.entries(node)) {
|
|
57
57
|
if (SCHEMA_MAP_KEYS.has(key)) {
|
|
58
|
-
if (isRecord$
|
|
58
|
+
if (isRecord$6(value)) for (const name of Object.keys(value)) visit(value[name], [key, name]);
|
|
59
59
|
continue;
|
|
60
60
|
}
|
|
61
61
|
if (SCHEMA_LIST_KEYS.has(key)) {
|
|
@@ -74,7 +74,7 @@ function pathKey(path) {
|
|
|
74
74
|
* `#` 解析到最近的带 `$id` 的祖先;没有则是文档根。
|
|
75
75
|
*/
|
|
76
76
|
function collectSelfRefRoots(node, path, resourceRoot, out) {
|
|
77
|
-
if (!isRecord$
|
|
77
|
+
if (!isRecord$6(node)) return;
|
|
78
78
|
if (isSelfRef(node)) {
|
|
79
79
|
out.set(pathKey(resourceRoot), resourceRoot);
|
|
80
80
|
return;
|
|
@@ -87,7 +87,7 @@ function collectSelfRefRoots(node, path, resourceRoot, out) {
|
|
|
87
87
|
function getAt(schema, path) {
|
|
88
88
|
let current = schema;
|
|
89
89
|
for (const segment of path) if (Array.isArray(current) && typeof segment === "number") current = current[segment];
|
|
90
|
-
else if (isRecord$
|
|
90
|
+
else if (isRecord$6(current) && typeof segment === "string") current = current[segment];
|
|
91
91
|
else return void 0;
|
|
92
92
|
return current;
|
|
93
93
|
}
|
|
@@ -113,7 +113,7 @@ function setAt(schema, path, replacement) {
|
|
|
113
113
|
* 正是严格解析器无法解析 `#` 的直接原因。
|
|
114
114
|
*/
|
|
115
115
|
function rewriteSelfRefs(node, target, depth) {
|
|
116
|
-
if (!isRecord$
|
|
116
|
+
if (!isRecord$6(node)) return node;
|
|
117
117
|
if (depth > 8) throw new WebSkillError("TOOL_SCHEMA_UNAVAILABLE", `Tool schema nesting exceeds 8 levels; cannot produce a portable form`);
|
|
118
118
|
if (isSelfRef(node)) {
|
|
119
119
|
const rest = {};
|
|
@@ -126,7 +126,7 @@ function rewriteSelfRefs(node, target, depth) {
|
|
|
126
126
|
const result = {};
|
|
127
127
|
for (const [key, value] of Object.entries(node)) {
|
|
128
128
|
if (key === "$id" || key === "$schema") continue;
|
|
129
|
-
if (SCHEMA_MAP_KEYS.has(key) && isRecord$
|
|
129
|
+
if (SCHEMA_MAP_KEYS.has(key) && isRecord$6(value)) {
|
|
130
130
|
const mapped = {};
|
|
131
131
|
for (const [name, child] of Object.entries(value)) mapped[name] = rewriteSelfRefs(child, target, depth + 1);
|
|
132
132
|
result[key] = mapped;
|
|
@@ -164,8 +164,8 @@ function toPortableToolSchema(schema) {
|
|
|
164
164
|
if (roots.size > 1) throw new WebSkillError("TOOL_SCHEMA_UNAVAILABLE", "Tool schema contains self references in more than one schema resource; cannot produce a portable form");
|
|
165
165
|
const rootPath = [...roots.values()][0] ?? [];
|
|
166
166
|
const resource = getAt(schema, rootPath);
|
|
167
|
-
if (!isRecord$
|
|
168
|
-
const existingDefs = isRecord$
|
|
167
|
+
if (!isRecord$6(resource)) return schema;
|
|
168
|
+
const existingDefs = isRecord$6(schema[DEFS_KEY]) ? schema[DEFS_KEY] : void 0;
|
|
169
169
|
const taken = new Set(Object.keys(existingDefs ?? {}));
|
|
170
170
|
const nodeName = uniqueName(NODE_NAME, taken);
|
|
171
171
|
taken.add(nodeName);
|
|
@@ -196,6 +196,136 @@ function toPortableToolSchema(schema) {
|
|
|
196
196
|
}
|
|
197
197
|
};
|
|
198
198
|
}
|
|
199
|
+
/** 各家都认的基础关键字。Google `FunctionDeclaration.parameters` 是其中最窄的一家。 */
|
|
200
|
+
const BASE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
201
|
+
"type",
|
|
202
|
+
"description",
|
|
203
|
+
"title",
|
|
204
|
+
"default",
|
|
205
|
+
"enum",
|
|
206
|
+
"const",
|
|
207
|
+
"properties",
|
|
208
|
+
"required",
|
|
209
|
+
"items",
|
|
210
|
+
"anyOf",
|
|
211
|
+
"oneOf",
|
|
212
|
+
"minimum",
|
|
213
|
+
"maximum",
|
|
214
|
+
"minItems",
|
|
215
|
+
"maxItems",
|
|
216
|
+
"nullable",
|
|
217
|
+
"format"
|
|
218
|
+
]);
|
|
219
|
+
/** 宽松侧:保留 0.6.0 的透传行为,本版不主动收紧(没有实证之前收紧只会制造新的破坏) */
|
|
220
|
+
const PERMISSIVE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
221
|
+
...BASE_KEYWORDS,
|
|
222
|
+
"additionalProperties",
|
|
223
|
+
"patternProperties",
|
|
224
|
+
"dependentSchemas",
|
|
225
|
+
"definitions",
|
|
226
|
+
"$defs",
|
|
227
|
+
"$ref",
|
|
228
|
+
"$schema",
|
|
229
|
+
"$id",
|
|
230
|
+
"allOf",
|
|
231
|
+
"not",
|
|
232
|
+
"if",
|
|
233
|
+
"then",
|
|
234
|
+
"else",
|
|
235
|
+
"prefixItems",
|
|
236
|
+
"additionalItems",
|
|
237
|
+
"unevaluatedItems",
|
|
238
|
+
"unevaluatedProperties",
|
|
239
|
+
"contains",
|
|
240
|
+
"propertyNames",
|
|
241
|
+
"exclusiveMinimum",
|
|
242
|
+
"exclusiveMaximum",
|
|
243
|
+
"multipleOf",
|
|
244
|
+
"minLength",
|
|
245
|
+
"maxLength",
|
|
246
|
+
"pattern",
|
|
247
|
+
"uniqueItems",
|
|
248
|
+
"examples",
|
|
249
|
+
"readOnly",
|
|
250
|
+
"writeOnly",
|
|
251
|
+
"deprecated"
|
|
252
|
+
]);
|
|
253
|
+
const OPENAI_SCHEMA_PROFILE = {
|
|
254
|
+
id: "openai-compatible",
|
|
255
|
+
allowedKeywords: PERMISSIVE_KEYWORDS,
|
|
256
|
+
supportsRefs: true
|
|
257
|
+
};
|
|
258
|
+
const ANTHROPIC_SCHEMA_PROFILE = {
|
|
259
|
+
id: "anthropic",
|
|
260
|
+
allowedKeywords: PERMISSIVE_KEYWORDS,
|
|
261
|
+
supportsRefs: true
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Google Gen AI 的 `FunctionDeclaration.parameters` 收到 `additionalProperties`
|
|
265
|
+
* 直接回 400 `Unknown name "additionalProperties"`,且不解析 `$ref` / `$defs`。
|
|
266
|
+
*/
|
|
267
|
+
const GOOGLE_SCHEMA_PROFILE = {
|
|
268
|
+
id: "google",
|
|
269
|
+
allowedKeywords: BASE_KEYWORDS,
|
|
270
|
+
supportsRefs: false
|
|
271
|
+
};
|
|
272
|
+
function isRecord$5(value) {
|
|
273
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
274
|
+
}
|
|
275
|
+
/** 只在 schema 位置上找 `$ref`:enum / const / default 里的同名键是字面数据,不是引用 */
|
|
276
|
+
function containsRef(node) {
|
|
277
|
+
if (Array.isArray(node)) return node.some(containsRef);
|
|
278
|
+
if (!isRecord$5(node)) return false;
|
|
279
|
+
if (typeof node["$ref"] === "string") return true;
|
|
280
|
+
for (const [key, value] of Object.entries(node)) {
|
|
281
|
+
if (SCHEMA_MAP_KEYS.has(key)) {
|
|
282
|
+
if (isRecord$5(value) && Object.values(value).some(containsRef)) return true;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (SCHEMA_LIST_KEYS.has(key)) {
|
|
286
|
+
if (Array.isArray(value) && value.some(containsRef)) return true;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (SCHEMA_KEYS.has(key) && containsRef(value)) return true;
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
function sanitizeNode(node, profile) {
|
|
294
|
+
if (Array.isArray(node)) return node.map((item) => sanitizeNode(item, profile));
|
|
295
|
+
if (!isRecord$5(node)) return node;
|
|
296
|
+
const out = {};
|
|
297
|
+
for (const [key, value] of Object.entries(node)) {
|
|
298
|
+
if (!profile.allowedKeywords.has(key)) continue;
|
|
299
|
+
if (SCHEMA_MAP_KEYS.has(key) && isRecord$5(value)) {
|
|
300
|
+
const mapped = {};
|
|
301
|
+
for (const [name, child] of Object.entries(value)) mapped[name] = sanitizeNode(child, profile);
|
|
302
|
+
out[key] = mapped;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (SCHEMA_LIST_KEYS.has(key) && Array.isArray(value)) {
|
|
306
|
+
out[key] = value.map((child) => sanitizeNode(child, profile));
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (SCHEMA_KEYS.has(key)) {
|
|
310
|
+
out[key] = sanitizeNode(value, profile);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
out[key] = value;
|
|
314
|
+
}
|
|
315
|
+
return out;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* 按供应商 profile 清洗出站 schema。产出新对象,不修改入参(工具源可能复用同一个实例)。
|
|
319
|
+
*
|
|
320
|
+
* 这是**传输层适配**,只应在出站请求体上调用;不要下沉到 AgentLoop,
|
|
321
|
+
* 否则 trace / eventBus 观测到的 schema 会与技能作者写的不一致。
|
|
322
|
+
*
|
|
323
|
+
* @throws WebSkillError `TOOL_SCHEMA_UNAVAILABLE` — schema 依赖该供应商不支持的引用
|
|
324
|
+
*/
|
|
325
|
+
function sanitizeForVendor(schema, profile, toolName) {
|
|
326
|
+
if (!profile.supportsRefs && containsRef(schema)) throw new WebSkillError("TOOL_SCHEMA_UNAVAILABLE", `Tool "${toolName}" requires $ref support that provider "${profile.id}" does not accept`);
|
|
327
|
+
return sanitizeNode(schema, profile);
|
|
328
|
+
}
|
|
199
329
|
function createSseFrameReader() {
|
|
200
330
|
let buffer = "";
|
|
201
331
|
let data = [];
|
|
@@ -307,7 +437,7 @@ const toOpenAiTools = (tools) => tools.map((tool) => ({
|
|
|
307
437
|
function: {
|
|
308
438
|
name: tool.name,
|
|
309
439
|
...tool.description ? { description: tool.description } : {},
|
|
310
|
-
parameters: toPortableToolSchema(tool.inputSchema)
|
|
440
|
+
parameters: sanitizeForVendor(toPortableToolSchema(tool.inputSchema), OPENAI_SCHEMA_PROFILE, tool.name)
|
|
311
441
|
}
|
|
312
442
|
}));
|
|
313
443
|
const errorMessage$2 = (e) => e instanceof Error ? e.message : String(e);
|
|
@@ -564,7 +694,7 @@ function toAnthropicMessages(messages) {
|
|
|
564
694
|
const toAnthropicTools = (tools) => tools.map((tool) => ({
|
|
565
695
|
name: tool.name,
|
|
566
696
|
...tool.description ? { description: tool.description } : {},
|
|
567
|
-
input_schema: toPortableToolSchema(tool.inputSchema)
|
|
697
|
+
input_schema: sanitizeForVendor(toPortableToolSchema(tool.inputSchema), ANTHROPIC_SCHEMA_PROFILE, tool.name)
|
|
568
698
|
}));
|
|
569
699
|
/** Anthropic Messages API 客户端(零依赖 fetch;Node/浏览器通用) */
|
|
570
700
|
var AnthropicClient = class {
|
|
@@ -807,7 +937,7 @@ function toGenAiContents(messages) {
|
|
|
807
937
|
const toGenAiTools = (tools) => [{ functionDeclarations: tools.map((tool) => ({
|
|
808
938
|
name: tool.name,
|
|
809
939
|
...tool.description ? { description: tool.description } : {},
|
|
810
|
-
parameters: toPortableToolSchema(tool.inputSchema)
|
|
940
|
+
parameters: sanitizeForVendor(toPortableToolSchema(tool.inputSchema), GOOGLE_SCHEMA_PROFILE, tool.name)
|
|
811
941
|
})) }];
|
|
812
942
|
/** Google GenAI(generateContent / streamGenerateContent)客户端(零依赖 fetch) */
|
|
813
943
|
var GoogleGenAiClient = class {
|
|
@@ -1260,22 +1390,22 @@ function createScriptContext(deps) {
|
|
|
1260
1390
|
...onWarning ? { onWarning } : {}
|
|
1261
1391
|
};
|
|
1262
1392
|
}
|
|
1263
|
-
const isRecord$
|
|
1393
|
+
const isRecord$4 = (v) => typeof v === "object" && v !== null;
|
|
1264
1394
|
/**
|
|
1265
1395
|
* $chart 约定的形状校验:JSON content 的 data 含 $chart 键且形状合法 → ChartSpec;
|
|
1266
1396
|
* 任何畸形(kind 非法 / labels 非字符串数组 / series 项缺数值 data)→ undefined(忽略不炸)。
|
|
1267
1397
|
*/
|
|
1268
1398
|
function extractChartSpec(data) {
|
|
1269
|
-
if (!isRecord$
|
|
1399
|
+
if (!isRecord$4(data)) return void 0;
|
|
1270
1400
|
const raw = data["$chart"];
|
|
1271
|
-
if (!isRecord$
|
|
1401
|
+
if (!isRecord$4(raw)) return void 0;
|
|
1272
1402
|
const { kind, labels, series } = raw;
|
|
1273
1403
|
if (kind !== "bar" && kind !== "line" && kind !== "pie") return void 0;
|
|
1274
1404
|
if (!Array.isArray(labels) || !labels.every((l) => typeof l === "string")) return void 0;
|
|
1275
1405
|
if (!Array.isArray(series)) return void 0;
|
|
1276
1406
|
const validSeries = [];
|
|
1277
1407
|
for (const item of series) {
|
|
1278
|
-
if (!isRecord$
|
|
1408
|
+
if (!isRecord$4(item) || !Array.isArray(item["data"]) || !item["data"].every((n) => typeof n === "number")) return;
|
|
1279
1409
|
const name = item["name"];
|
|
1280
1410
|
validSeries.push({
|
|
1281
1411
|
...typeof name === "string" ? { name } : {},
|
|
@@ -1293,13 +1423,18 @@ function extractChartSpec(data) {
|
|
|
1293
1423
|
/**
|
|
1294
1424
|
* 默认的结果渲染构造:run 内收集的 renderBlocks(chart 等)在前,
|
|
1295
1425
|
* LLM 最终输出 → markdown block,run.artifacts → file blocks 在后;summary 取 terminationReason。
|
|
1426
|
+
* 注入 output block 时一并记下它的下标(S7),供消费方按来源去重。
|
|
1296
1427
|
*/
|
|
1297
1428
|
function buildRenderResult(run, output, renderBlocks = []) {
|
|
1298
1429
|
const blocks = [...renderBlocks];
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1430
|
+
let outputBlockIndex;
|
|
1431
|
+
if (output.trim() !== "") {
|
|
1432
|
+
outputBlockIndex = blocks.length;
|
|
1433
|
+
blocks.push({
|
|
1434
|
+
type: "markdown",
|
|
1435
|
+
text: output
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1303
1438
|
for (const artifact of run.artifacts) blocks.push({
|
|
1304
1439
|
type: "file",
|
|
1305
1440
|
path: artifact.path,
|
|
@@ -1310,7 +1445,8 @@ function buildRenderResult(run, output, renderBlocks = []) {
|
|
|
1310
1445
|
runId: run.id,
|
|
1311
1446
|
summary: run.terminationReason,
|
|
1312
1447
|
blocks,
|
|
1313
|
-
artifacts: run.artifacts
|
|
1448
|
+
artifacts: run.artifacts,
|
|
1449
|
+
...outputBlockIndex === void 0 ? {} : { outputBlockIndex }
|
|
1314
1450
|
};
|
|
1315
1451
|
}
|
|
1316
1452
|
const MAX_SURFACE_BYTES = 256 * 1024;
|
|
@@ -1326,7 +1462,7 @@ const actionIntents = /* @__PURE__ */ new Set([
|
|
|
1326
1462
|
"download",
|
|
1327
1463
|
"refresh"
|
|
1328
1464
|
]);
|
|
1329
|
-
function isRecord$
|
|
1465
|
+
function isRecord$3(value) {
|
|
1330
1466
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1331
1467
|
}
|
|
1332
1468
|
function reject(message) {
|
|
@@ -1343,7 +1479,7 @@ function isJsonValue(value, depth = 0) {
|
|
|
1343
1479
|
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
1344
1480
|
if (typeof value === "number") return Number.isFinite(value);
|
|
1345
1481
|
if (Array.isArray(value)) return value.every((item) => isJsonValue(item, depth + 1));
|
|
1346
|
-
if (!isRecord$
|
|
1482
|
+
if (!isRecord$3(value)) return false;
|
|
1347
1483
|
return Object.keys(value).every((key) => key !== "__proto__" && key !== "constructor" && isJsonValue(value[key], depth + 1));
|
|
1348
1484
|
}
|
|
1349
1485
|
/**
|
|
@@ -1353,10 +1489,10 @@ function isJsonValue(value, depth = 0) {
|
|
|
1353
1489
|
function assertNode(value, path, depth, counter) {
|
|
1354
1490
|
if (depth > MAX_NODE_DEPTH) reject(`A UI spec tree must not nest deeper than ${MAX_NODE_DEPTH} levels`);
|
|
1355
1491
|
if (++counter.nodes > MAX_NODES) reject(`A UI spec tree must contain at most ${MAX_NODES} nodes`);
|
|
1356
|
-
if (!isRecord$
|
|
1492
|
+
if (!isRecord$3(value)) reject(`${path} must be an object`);
|
|
1357
1493
|
requireString(value["component"], `${path}.component`);
|
|
1358
1494
|
if (value["id"] !== void 0) requireString(value["id"], `${path}.id`);
|
|
1359
|
-
if (value["props"] !== void 0 && (!isRecord$
|
|
1495
|
+
if (value["props"] !== void 0 && (!isRecord$3(value["props"]) || !isJsonValue(value["props"]))) reject(`${path}.props must be a JSON object`);
|
|
1360
1496
|
const children = value["children"];
|
|
1361
1497
|
if (children === void 0) return;
|
|
1362
1498
|
if (!Array.isArray(children)) reject(`${path}.children must be an array`);
|
|
@@ -1366,7 +1502,7 @@ function assertActions(value) {
|
|
|
1366
1502
|
if (value === void 0) return;
|
|
1367
1503
|
if (!Array.isArray(value) || value.length > MAX_ACTIONS) reject(`Surface actions must contain at most ${MAX_ACTIONS} items`);
|
|
1368
1504
|
for (const action of value) {
|
|
1369
|
-
if (!isRecord$
|
|
1505
|
+
if (!isRecord$3(action)) reject("A surface action must be an object");
|
|
1370
1506
|
requireString(action["id"], "Surface action ID");
|
|
1371
1507
|
const intent = action["intent"];
|
|
1372
1508
|
if (typeof intent !== "string" || !actionIntents.has(intent)) reject("Surface action intent is invalid");
|
|
@@ -1382,7 +1518,7 @@ function validateUiSpecNode(value) {
|
|
|
1382
1518
|
return structuredClone(value);
|
|
1383
1519
|
}
|
|
1384
1520
|
function assertPatch(value) {
|
|
1385
|
-
if (!isRecord$
|
|
1521
|
+
if (!isRecord$3(value)) reject("A surface patch operation must be an object");
|
|
1386
1522
|
if (value["op"] !== "replace" && value["op"] !== "merge" && value["op"] !== "append") reject("Surface patch operation is invalid");
|
|
1387
1523
|
requireString(value["path"], "Surface patch path");
|
|
1388
1524
|
if (!value["path"].startsWith("/")) reject("Surface patch path must be a JSON pointer");
|
|
@@ -1390,7 +1526,7 @@ function assertPatch(value) {
|
|
|
1390
1526
|
}
|
|
1391
1527
|
/** Validates an individual event in the framework-neutral surface stream. @experimental */
|
|
1392
1528
|
function validateUiSpecEvent(value) {
|
|
1393
|
-
if (!isRecord$
|
|
1529
|
+
if (!isRecord$3(value) || typeof value["type"] !== "string") reject("A UI surface event must have a type");
|
|
1394
1530
|
if (value["runId"] !== void 0) requireString(value["runId"], "Surface event run ID");
|
|
1395
1531
|
switch (value["type"]) {
|
|
1396
1532
|
case "open":
|
|
@@ -1448,7 +1584,7 @@ function validateUiSpecEvent(value) {
|
|
|
1448
1584
|
}
|
|
1449
1585
|
/** Extracts validated surface stream events from structured tool output. @experimental */
|
|
1450
1586
|
function extractUiSpecEvents(data) {
|
|
1451
|
-
if (!isRecord$
|
|
1587
|
+
if (!isRecord$3(data) || data["$surface"] === void 0) return [];
|
|
1452
1588
|
const raw = data["$surface"];
|
|
1453
1589
|
return (Array.isArray(raw) ? raw : [raw]).map((event) => validateUiSpecEvent(event));
|
|
1454
1590
|
}
|
|
@@ -1767,7 +1903,7 @@ var TraceRecorder = class {
|
|
|
1767
1903
|
return [...this.#events];
|
|
1768
1904
|
}
|
|
1769
1905
|
};
|
|
1770
|
-
const isRecord$
|
|
1906
|
+
const isRecord$2 = (v) => typeof v === "object" && v !== null;
|
|
1771
1907
|
/** 工具结果可经 `$todo` 标记记入 trace 的事件类型;其余类型不接受,防止工具源伪造 run.* */
|
|
1772
1908
|
const TODO_TRACE_TYPES = /* @__PURE__ */ new Set([
|
|
1773
1909
|
"todo.created",
|
|
@@ -1782,12 +1918,12 @@ const TODO_TRACE_TYPES = /* @__PURE__ */ new Set([
|
|
|
1782
1918
|
* @experimental
|
|
1783
1919
|
*/
|
|
1784
1920
|
function extractTodoTraceEvents(data) {
|
|
1785
|
-
if (!isRecord$
|
|
1921
|
+
if (!isRecord$2(data) || data["$todo"] === void 0) return [];
|
|
1786
1922
|
const raw = data["$todo"];
|
|
1787
1923
|
const entries = Array.isArray(raw) ? raw : [raw];
|
|
1788
1924
|
const events = [];
|
|
1789
1925
|
for (const entry of entries) {
|
|
1790
|
-
if (!isRecord$
|
|
1926
|
+
if (!isRecord$2(entry)) continue;
|
|
1791
1927
|
const { type, ...rest } = entry;
|
|
1792
1928
|
if (typeof type !== "string" || !TODO_TRACE_TYPES.has(type)) continue;
|
|
1793
1929
|
events.push({
|
|
@@ -1797,6 +1933,38 @@ function extractTodoTraceEvents(data) {
|
|
|
1797
1933
|
}
|
|
1798
1934
|
return events;
|
|
1799
1935
|
}
|
|
1936
|
+
const isRecord$1 = (v) => typeof v === "object" && v !== null;
|
|
1937
|
+
/**
|
|
1938
|
+
* `$skillCandidate` 约定的形状校验:JSON content 的 data 含合法 `$skillCandidate` 键 → 候选载荷。
|
|
1939
|
+
*
|
|
1940
|
+
* 与 `$chart` / `$todo` / `$surface` 同一条既有通道,是第四个。
|
|
1941
|
+
* 候选的生成与存储全部在 `@webskill/agent`,runtime 只认这个形状,畸形忽略不炸。
|
|
1942
|
+
* @experimental
|
|
1943
|
+
*/
|
|
1944
|
+
function extractSkillCandidate(data) {
|
|
1945
|
+
if (!isRecord$1(data)) return void 0;
|
|
1946
|
+
const raw = data["$skillCandidate"];
|
|
1947
|
+
if (!isRecord$1(raw)) return void 0;
|
|
1948
|
+
const { id, name } = raw;
|
|
1949
|
+
if (typeof id !== "string" || id === "" || typeof name !== "string" || name === "") return void 0;
|
|
1950
|
+
return {
|
|
1951
|
+
id,
|
|
1952
|
+
name
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Agent loop 的运行上限默认值。**全仓唯一来源**(S8)。
|
|
1957
|
+
*
|
|
1958
|
+
* 在 0.7.0 之前这三个数字在 `AgentLoop` 的构造与 ui-kit 的 `defaultRuntimeConfig()`
|
|
1959
|
+
* 里各写了一遍。两份值今天恰好相等,所以不出事;一旦其中一处被改,
|
|
1960
|
+
* 「恢复默认值」会恢复到 agent loop 根本不用的值——验收绿、行为错。
|
|
1961
|
+
* @stable
|
|
1962
|
+
*/
|
|
1963
|
+
const DEFAULT_LOOP_LIMITS = {
|
|
1964
|
+
maxTurns: 10,
|
|
1965
|
+
totalTimeoutMs: 12e4,
|
|
1966
|
+
toolTimeoutMs: 3e4
|
|
1967
|
+
};
|
|
1800
1968
|
const RUN_SNAPSHOT_SCHEMA_VERSION = 2;
|
|
1801
1969
|
/** @experimental */
|
|
1802
1970
|
function isUnsupportedRunSnapshot(entry) {
|
|
@@ -1975,6 +2143,20 @@ const redactFileValue = (value) => {
|
|
|
1975
2143
|
return rest;
|
|
1976
2144
|
};
|
|
1977
2145
|
/**
|
|
2146
|
+
* 触顶失败的结构化细节(FR-21.2)。由终止原因推出上限字段,而不是在每个终止点各写一遍——
|
|
2147
|
+
* 终止点有四个,写四遍就迟早有一处对不上。
|
|
2148
|
+
*/
|
|
2149
|
+
const limitDetails = (state, reason) => {
|
|
2150
|
+
if (reason === "timeout") return {
|
|
2151
|
+
limit: "totalTimeoutMs",
|
|
2152
|
+
value: state.totalTimeoutMs
|
|
2153
|
+
};
|
|
2154
|
+
if (reason === "max-turns") return {
|
|
2155
|
+
limit: "maxTurns",
|
|
2156
|
+
value: state.maxTurns
|
|
2157
|
+
};
|
|
2158
|
+
};
|
|
2159
|
+
/**
|
|
1978
2160
|
* 多轮 Agent 循环。
|
|
1979
2161
|
* 工具失败一律转为 ToolResult{ok:false} 回喂 LLM 继续循环;
|
|
1980
2162
|
* 缺参/确认/提问经 UiBridge 行内 await 暂停恢复;取消/超时/LLM 异常/护栏超限才终止 run。
|
|
@@ -1993,9 +2175,9 @@ var AgentLoop = class {
|
|
|
1993
2175
|
artifactStore: deps.artifactStore ?? new MemoryArtifactStore()
|
|
1994
2176
|
};
|
|
1995
2177
|
this.#config = {
|
|
1996
|
-
maxTurns: config.maxTurns ??
|
|
1997
|
-
totalTimeoutMs: config.totalTimeoutMs ??
|
|
1998
|
-
toolTimeoutMs: config.toolTimeoutMs ??
|
|
2178
|
+
maxTurns: config.maxTurns ?? DEFAULT_LOOP_LIMITS.maxTurns,
|
|
2179
|
+
totalTimeoutMs: config.totalTimeoutMs ?? DEFAULT_LOOP_LIMITS.totalTimeoutMs,
|
|
2180
|
+
toolTimeoutMs: config.toolTimeoutMs ?? DEFAULT_LOOP_LIMITS.toolTimeoutMs,
|
|
1999
2181
|
toolResultMaxBytes: config.toolResultMaxBytes ?? 1e5,
|
|
2000
2182
|
paramHistoryLimit: config.paramHistoryLimit ?? 50,
|
|
2001
2183
|
toolCallingDisabled: config.toolCallingDisabled ?? false,
|
|
@@ -2210,6 +2392,7 @@ var AgentLoop = class {
|
|
|
2210
2392
|
return finish("failed", "llm-error", messageOf(e), code);
|
|
2211
2393
|
}
|
|
2212
2394
|
const responseText = partsToText(response.content);
|
|
2395
|
+
if (!llm.stream && responseText !== "") await this.#deps.uiBridge?.onTextDelta?.(state.runId, responseText);
|
|
2213
2396
|
trace.record("llm.response", { data: {
|
|
2214
2397
|
turn,
|
|
2215
2398
|
hasToolCalls: Boolean(response.toolCalls?.length),
|
|
@@ -2279,7 +2462,8 @@ var AgentLoop = class {
|
|
|
2279
2462
|
message: output,
|
|
2280
2463
|
data: {
|
|
2281
2464
|
reason,
|
|
2282
|
-
...errorCode ? { code: errorCode } : {}
|
|
2465
|
+
...errorCode ? { code: errorCode } : {},
|
|
2466
|
+
...limitDetails(state, reason) ?? {}
|
|
2283
2467
|
}
|
|
2284
2468
|
});
|
|
2285
2469
|
const bridge = this.#deps.uiBridge;
|
|
@@ -2721,6 +2905,12 @@ var AgentLoop = class {
|
|
|
2721
2905
|
chart
|
|
2722
2906
|
});
|
|
2723
2907
|
for (const todo of extractTodoTraceEvents(item.data)) state.trace.record(todo.type, { data: todo.data });
|
|
2908
|
+
const candidate = extractSkillCandidate(item.data);
|
|
2909
|
+
if (candidate) try {
|
|
2910
|
+
await this.#deps.uiBridge?.onSkillCandidate?.(state.runId, candidate);
|
|
2911
|
+
} catch (e) {
|
|
2912
|
+
state.trace.record("run.warning", { message: `Skill candidate was not handed off to the UI: ${messageOf(e)}` });
|
|
2913
|
+
}
|
|
2724
2914
|
try {
|
|
2725
2915
|
for (const event of extractUiSpecEvents(item.data)) await this.#renderSurface(state, event);
|
|
2726
2916
|
} catch (e) {
|
|
@@ -3332,7 +3522,8 @@ var AgentLoop = class {
|
|
|
3332
3522
|
if (fresh.length > 0) result.artifacts = [...result.artifacts ?? [], ...fresh];
|
|
3333
3523
|
}
|
|
3334
3524
|
await this.#bumpSkillStat(skillName, result.ok ? "successes" : "failures", state);
|
|
3335
|
-
if (
|
|
3525
|
+
if (result.ok) await this.#reportSkillSuccess(skillName, state);
|
|
3526
|
+
else await this.#reportSkillFailure(skillName, state, {
|
|
3336
3527
|
code: result.error?.code ?? "TOOL_EXECUTION_FAILED",
|
|
3337
3528
|
message: result.error?.message ?? `Tool "${call.name}" returned a failure result`
|
|
3338
3529
|
});
|
|
@@ -3369,6 +3560,22 @@ var AgentLoop = class {
|
|
|
3369
3560
|
state.trace.record("run.warning", { message: `Skill failure report for "${skillName}" was not recorded: ${messageOf(e)}` });
|
|
3370
3561
|
}
|
|
3371
3562
|
}
|
|
3563
|
+
/**
|
|
3564
|
+
* D-5 技能成功上报:把失败计数从「累计」变成「连续」的那半边信号。
|
|
3565
|
+
* 与失败上报同样是可选 port,未注入即整段跳过。
|
|
3566
|
+
*/
|
|
3567
|
+
async #reportSkillSuccess(skillName, state) {
|
|
3568
|
+
const report = this.#deps.skillOutcomeReporter?.onSkillSucceeded;
|
|
3569
|
+
if (report === void 0) return;
|
|
3570
|
+
try {
|
|
3571
|
+
await report.call(this.#deps.skillOutcomeReporter, {
|
|
3572
|
+
skillName,
|
|
3573
|
+
runId: state.runId
|
|
3574
|
+
});
|
|
3575
|
+
} catch (e) {
|
|
3576
|
+
state.trace.record("run.warning", { message: `Skill success report for "${skillName}" was not recorded: ${messageOf(e)}` });
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3372
3579
|
/** context.confirm 触发点:默认真实询问;auto-approve 直通;无 bridge 降级直通 + warning */
|
|
3373
3580
|
async #confirm(message, state) {
|
|
3374
3581
|
if (this.#policy.confirmations === "auto-approve") return true;
|
|
@@ -4972,4 +5179,4 @@ var FsSessionStore = class {
|
|
|
4972
5179
|
};
|
|
4973
5180
|
|
|
4974
5181
|
//#endregion
|
|
4975
|
-
export {
|
|
5182
|
+
export { isNetworkAllowed as $, SerializingMemoryStore as A, bridgeError as B, ProgressiveRouter as C, toRecordDigests as Ct, RUN_SNAPSHOT_SCHEMA_VERSION as D, READ_SKILL_FILE_TOOL_NAME as E, validateUiSpecNode as Et, USER_PROFILE_PROMPT_HEADER as F, exportUserProfile as G, createScriptContext as H, USER_PROFILE_REFINE_PROMPT as I, extractTodoTraceEvents as J, extractChartSpec as K, WebSkillRuntime as L, USER_PROFILE_EXPORT_VERSION as M, USER_PROFILE_KEY as N, RUN_TRACE_SCHEMA_VERSION as O, USER_PROFILE_NO_INVENTION_RULE as P, fromVercelStreamPart as Q, appendBehaviorRecords as R, OpenAiCompatibleClient as S, toLlmToolSpec as St, READ_SKILL_FILE_TOOL as T, validateUiSpecEvent as Tt, createWebSkillApi as U, buildRenderResult as V, diffUserProfile as W, formatSkillScriptManifest as X, extractUiSpecEvents as Y, fromVercelResult as Z, FsRunTraceStore as _, sampleBehaviorRecords as _t, AgentLoop as a, networkUrlHost as at, GoogleGenAiClient as b, scriptToolName as bt, CapabilityApproval as c, normalizeToolError as ct, EMPTY_USER_PROFILE as d, readBehaviorRecords as dt, isUnsupportedRunSnapshot as et, EventBus as f, readProfileEntries as ft, FsRunSnapshotStore as g, resolveToolName as gt, FsMemoryStore as h, renderUserProfileContext as ht, ASK_USER_TOOL_NAME as i, networkPolicyLibSource as it, TraceRecorder as j, SESSION_SCHEMA_VERSION as k, DEFAULT_LOOP_LIMITS as l, parseBridgeRequest as lt, FsArtifactStore as m, refineUserProfile as mt, ASK_USER_INPUT_SCHEMA as n, mergeCatalogEntries as nt, AnthropicClient as o, normalizeErrorCode as ot, FS_SESSION_PAGE_SIZE as p, readUserProfile as pt, extractSkillCandidate as q, ASK_USER_TOOL as r, mergeProfileEntries as rt, BEHAVIOR_RECORDS_KEY as s, normalizeToolContent as st, ALLOWED_TOOLS_EXCLUSION_REASON as t, listSkillScripts as tt, DEFAULT_USER_PROFILE_LIMITS as u, parseUserProfileExport as ut, FsSessionStore as v, schemaSourceLabel as vt, READ_SKILL_FILE_INPUT_SCHEMA as w, toVercelToolSpecs as wt, HookRunner as x, summarizeToolCalls as xt, FullDisclosureRouter as y, schemaToForm as yt, applyUserProfileImport as z };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region ../governance/dist/eventTypes-DjIQpt8Y.js
|
|
2
|
+
/**
|
|
3
|
+
* 审计事件类型常量(FR-13.4)。
|
|
4
|
+
*
|
|
5
|
+
* 存在的理由是**消灭调用点的字面量**:写入点分散在 governance 与 console 的十来个文件里,
|
|
6
|
+
* 字面量拼错不会有任何编译期反馈,只会在审计里留下一条永远查不到的记录。
|
|
7
|
+
*
|
|
8
|
+
* `AuditEvent.type` 刻意**保持 `string`**、不收窄为 `AuditEventType`——
|
|
9
|
+
* 收窄会让外部写入方(插件、其它宿主)无法追加自定义类型,且会立刻产生快照差异。
|
|
10
|
+
*/
|
|
11
|
+
const AUDIT_EVENT_TYPES = {
|
|
12
|
+
skillEdited: "skill.edited",
|
|
13
|
+
skillRolledBack: "skill.rolled_back",
|
|
14
|
+
skillPublished: "skill.published",
|
|
15
|
+
skillRepairApplied: "skill.repair_applied",
|
|
16
|
+
skillQuarantined: "skill.quarantined",
|
|
17
|
+
skillPolicyDenied: "skill.policy_denied",
|
|
18
|
+
skillUninstalled: "skill.uninstalled",
|
|
19
|
+
skillInstalled: "skill.installed",
|
|
20
|
+
trustKeyAdded: "trust.key_added",
|
|
21
|
+
trustKeyRemoved: "trust.key_removed",
|
|
22
|
+
policyNetworkChanged: "policy.network_changed",
|
|
23
|
+
policyPrivacyChanged: "policy.privacy_changed",
|
|
24
|
+
mcpEndpointChanged: "mcp.endpoint_changed",
|
|
25
|
+
mcpPolicyRelaxed: "mcp.policy_relaxed",
|
|
26
|
+
providerChanged: "provider.changed"
|
|
27
|
+
};
|
|
28
|
+
/** 稳定展示序:筛选下拉与文档表格都从这里取,不各自维护一份顺序 */
|
|
29
|
+
const AUDIT_EVENT_TYPE_LIST = Object.values(AUDIT_EVENT_TYPES);
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { AUDIT_EVENT_TYPE_LIST as n, AUDIT_EVENT_TYPES as t };
|