@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.
@@ -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$4(value) {
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$4(value) && value["$ref"] === SELF_REF;
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$4(value)) for (const name of Object.keys(value)) visit(value[name], [key, name]);
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$4(node)) return;
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$4(current) && typeof segment === "string") current = current[segment];
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$4(node)) return node;
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$4(value)) {
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$4(resource)) return schema;
168
- const existingDefs = isRecord$4(schema[DEFS_KEY]) ? schema[DEFS_KEY] : void 0;
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$3 = (v) => typeof v === "object" && v !== null;
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$3(data)) return void 0;
1399
+ if (!isRecord$4(data)) return void 0;
1270
1400
  const raw = data["$chart"];
1271
- if (!isRecord$3(raw)) return void 0;
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$3(item) || !Array.isArray(item["data"]) || !item["data"].every((n) => typeof n === "number")) return;
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
- if (output.trim() !== "") blocks.push({
1300
- type: "markdown",
1301
- text: output
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$2(value) {
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$2(value)) return false;
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$2(value)) reject(`${path} must be an object`);
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$2(value["props"]) || !isJsonValue(value["props"]))) reject(`${path}.props must be a JSON object`);
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$2(action)) reject("A surface action must be an object");
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$2(value)) reject("A surface patch operation must be an object");
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$2(value) || typeof value["type"] !== "string") reject("A UI surface event must have a type");
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$2(data) || data["$surface"] === void 0) return [];
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$1 = (v) => typeof v === "object" && v !== null;
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$1(data) || data["$todo"] === void 0) return [];
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$1(entry)) continue;
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 ?? 10,
1997
- totalTimeoutMs: config.totalTimeoutMs ?? 12e4,
1998
- toolTimeoutMs: config.toolTimeoutMs ?? 3e4,
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 (!result.ok) await this.#reportSkillFailure(skillName, state, {
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 { listSkillScripts as $, TraceRecorder as A, buildRenderResult as B, READ_SKILL_FILE_INPUT_SCHEMA as C, validateUiSpecEvent as Ct, RUN_TRACE_SCHEMA_VERSION as D, RUN_SNAPSHOT_SCHEMA_VERSION as E, USER_PROFILE_REFINE_PROMPT as F, extractChartSpec as G, createWebSkillApi as H, WebSkillRuntime as I, formatSkillScriptManifest as J, extractTodoTraceEvents as K, appendBehaviorRecords as L, USER_PROFILE_KEY as M, USER_PROFILE_NO_INVENTION_RULE as N, SESSION_SCHEMA_VERSION as O, USER_PROFILE_PROMPT_HEADER as P, isUnsupportedRunSnapshot as Q, applyUserProfileImport as R, ProgressiveRouter as S, toVercelToolSpecs as St, READ_SKILL_FILE_TOOL_NAME as T, diffUserProfile as U, createScriptContext as V, exportUserProfile as W, fromVercelStreamPart as X, fromVercelResult as Y, isNetworkAllowed as Z, FsSessionStore as _, schemaToForm as _t, AgentLoop as a, normalizeToolContent as at, HookRunner as b, toLlmToolSpec as bt, CapabilityApproval as c, parseUserProfileExport as ct, EventBus as d, readUserProfile as dt, mergeCatalogEntries as et, FS_SESSION_PAGE_SIZE as f, refineUserProfile as ft, FsRunTraceStore as g, schemaSourceLabel as gt, FsRunSnapshotStore as h, sampleBehaviorRecords as ht, ASK_USER_TOOL_NAME as i, normalizeErrorCode as it, USER_PROFILE_EXPORT_VERSION as j, SerializingMemoryStore as k, DEFAULT_USER_PROFILE_LIMITS as l, readBehaviorRecords as lt, FsMemoryStore as m, resolveToolName as mt, ASK_USER_INPUT_SCHEMA as n, networkPolicyLibSource as nt, AnthropicClient as o, normalizeToolError as ot, FsArtifactStore as p, renderUserProfileContext as pt, extractUiSpecEvents as q, ASK_USER_TOOL as r, networkUrlHost as rt, BEHAVIOR_RECORDS_KEY as s, parseBridgeRequest as st, ALLOWED_TOOLS_EXCLUSION_REASON as t, mergeProfileEntries as tt, EMPTY_USER_PROFILE as u, readProfileEntries as ut, FullDisclosureRouter as v, scriptToolName as vt, READ_SKILL_FILE_TOOL as w, validateUiSpecNode as wt, OpenAiCompatibleClient as x, toRecordDigests as xt, GoogleGenAiClient as y, summarizeToolCalls as yt, bridgeError as z };
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 };