@webskill/sdk 0.1.2 → 0.1.4
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/README.md +5 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +54 -22
- package/dist/{dist-SarUS8EP.js → dist-BVXZF7H_.js} +177 -58
- package/dist/{dist-DRsnVw6A.js → dist-DAn07zHu.js} +452 -11
- package/dist/{dist-DS1sfgHa.js → dist-DvoBsChX.js} +13 -1
- package/dist/{dist-DxGyAznR.js → dist-MTc2KB03.js} +46 -3
- package/dist/governance.d.ts +7 -4
- package/dist/governance.js +19 -4
- package/dist/{index-fjRF4H5o.d.ts → index-BSS3EWY-.d.ts} +48 -2
- package/dist/{index-DkPK44Ji.d.ts → index-npFMt2Zw.d.ts} +27 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +25 -3
- package/dist/mcp.js +58 -13
- package/dist/node.d.ts +4 -4
- package/dist/node.js +4 -4
- package/dist/sandboxWorkerEntry.d.ts +1 -0
- package/dist/sandboxWorkerEntry.js +214 -0
- package/dist/{testing-CbM6rJ-E.js → testing-S8SN9Lc6.js} +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{types-CgNC-oQu-Dq_A1yA-.d.ts → types-CgNC-oQu-DYrxhD3z.d.ts} +11 -1
- package/dist/ui-react.d.ts +1 -1
- package/dist/ui-react.js +1 -1
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui-vue.js +1 -1
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as renderAvailableSkillsXml, E as validateSkills, T as resolveInsideRoot, c as SkillReader, d as buildCatalog, l as WebSkillError, s as SkillDiscovery, u as assertSafePathSegment, x as parseSkillMarkdown } from "./dist-DvoBsChX.js";
|
|
2
2
|
import { t as MemoryArtifactStore } from "./memoryArtifactStore-C9lFVqPF-yFz6yJj0.js";
|
|
3
3
|
|
|
4
4
|
//#region ../runtime/dist/index.js
|
|
@@ -33,7 +33,7 @@ const toOpenAiTools = (tools) => tools.map((tool) => ({
|
|
|
33
33
|
parameters: tool.inputSchema
|
|
34
34
|
}
|
|
35
35
|
}));
|
|
36
|
-
const errorMessage = (e) => e instanceof Error ? e.message : String(e);
|
|
36
|
+
const errorMessage$2 = (e) => e instanceof Error ? e.message : String(e);
|
|
37
37
|
/** OpenAI chat completions 协议客户端(兼容端点通用) */
|
|
38
38
|
var OpenAiCompatibleClient = class {
|
|
39
39
|
#config;
|
|
@@ -57,7 +57,7 @@ var OpenAiCompatibleClient = class {
|
|
|
57
57
|
try {
|
|
58
58
|
data = await res.json();
|
|
59
59
|
} catch (e) {
|
|
60
|
-
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse LLM response: ${errorMessage(e)}`, e);
|
|
60
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse LLM response: ${errorMessage$2(e)}`, e);
|
|
61
61
|
}
|
|
62
62
|
return this.#parseResponse(data);
|
|
63
63
|
}
|
|
@@ -79,7 +79,7 @@ var OpenAiCompatibleClient = class {
|
|
|
79
79
|
try {
|
|
80
80
|
chunk = JSON.parse(data);
|
|
81
81
|
} catch (e) {
|
|
82
|
-
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse LLM stream frame: ${errorMessage(e)}`, data);
|
|
82
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse LLM stream frame: ${errorMessage$2(e)}`, data);
|
|
83
83
|
}
|
|
84
84
|
const delta = chunk.choices?.[0]?.delta;
|
|
85
85
|
if (!delta) return;
|
|
@@ -161,7 +161,7 @@ var OpenAiCompatibleClient = class {
|
|
|
161
161
|
signal: input.signal ?? (this.#config.requestTimeoutMs ? AbortSignal.timeout(this.#config.requestTimeoutMs) : null)
|
|
162
162
|
});
|
|
163
163
|
} catch (e) {
|
|
164
|
-
throw new WebSkillError("LLM_REQUEST_FAILED", `LLM request failed: ${errorMessage(e)}`, e);
|
|
164
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `LLM request failed: ${errorMessage$2(e)}`, e);
|
|
165
165
|
}
|
|
166
166
|
if (!res.ok) {
|
|
167
167
|
const detail = await res.text().catch(() => "");
|
|
@@ -196,10 +196,433 @@ var OpenAiCompatibleClient = class {
|
|
|
196
196
|
toolCalls: toolCalls?.length ? toolCalls : void 0,
|
|
197
197
|
raw: data
|
|
198
198
|
};
|
|
199
|
+
} catch (e) {
|
|
200
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to interpret LLM response: ${errorMessage$2(e)}`, data);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const ANTHROPIC_VERSION = "2023-06-01";
|
|
205
|
+
const errorMessage$1 = (e) => e instanceof Error ? e.message : String(e);
|
|
206
|
+
/** LlmMessage 序列 → system 独立参数 + user/assistant 消息(tool 结果合并进 user 消息 tool_result blocks) */
|
|
207
|
+
function toAnthropicMessages(messages) {
|
|
208
|
+
const system = messages.filter((m) => m.role === "system").map((m) => m.content).join("\n");
|
|
209
|
+
const out = [];
|
|
210
|
+
for (const msg of messages) {
|
|
211
|
+
if (msg.role === "system") continue;
|
|
212
|
+
if (msg.role === "tool") {
|
|
213
|
+
const block = {
|
|
214
|
+
type: "tool_result",
|
|
215
|
+
tool_use_id: msg.toolCallId ?? "",
|
|
216
|
+
content: msg.content
|
|
217
|
+
};
|
|
218
|
+
const last = out.at(-1);
|
|
219
|
+
if (last && last.role === "user" && Array.isArray(last.content)) last.content.push(block);
|
|
220
|
+
else out.push({
|
|
221
|
+
role: "user",
|
|
222
|
+
content: [block]
|
|
223
|
+
});
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (msg.role === "assistant" && msg.toolCalls?.length) {
|
|
227
|
+
const content = [];
|
|
228
|
+
if (msg.content !== "") content.push({
|
|
229
|
+
type: "text",
|
|
230
|
+
text: msg.content
|
|
231
|
+
});
|
|
232
|
+
for (const call of msg.toolCalls) content.push({
|
|
233
|
+
type: "tool_use",
|
|
234
|
+
id: call.id,
|
|
235
|
+
name: call.name,
|
|
236
|
+
input: call.arguments
|
|
237
|
+
});
|
|
238
|
+
out.push({
|
|
239
|
+
role: "assistant",
|
|
240
|
+
content
|
|
241
|
+
});
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
out.push({
|
|
245
|
+
role: msg.role,
|
|
246
|
+
content: msg.content
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return system === "" ? { messages: out } : {
|
|
250
|
+
system,
|
|
251
|
+
messages: out
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
const toAnthropicTools = (tools) => tools.map((tool) => ({
|
|
255
|
+
name: tool.name,
|
|
256
|
+
...tool.description ? { description: tool.description } : {},
|
|
257
|
+
input_schema: tool.inputSchema
|
|
258
|
+
}));
|
|
259
|
+
/** Anthropic Messages API 客户端(零依赖 fetch;Node/浏览器通用) */
|
|
260
|
+
var AnthropicClient = class {
|
|
261
|
+
#config;
|
|
262
|
+
#fetch;
|
|
263
|
+
constructor(config) {
|
|
264
|
+
this.#config = config;
|
|
265
|
+
this.#fetch = config.fetchImpl ?? fetch;
|
|
266
|
+
}
|
|
267
|
+
#baseUrl() {
|
|
268
|
+
return (this.#config.baseUrl ?? "https://api.anthropic.com").replace(/\/+$/, "");
|
|
269
|
+
}
|
|
270
|
+
#headers() {
|
|
271
|
+
return {
|
|
272
|
+
"content-type": "application/json",
|
|
273
|
+
"x-api-key": this.#config.apiKey,
|
|
274
|
+
"anthropic-version": ANTHROPIC_VERSION,
|
|
275
|
+
"anthropic-dangerous-direct-browser-access": "true"
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
async complete(input) {
|
|
279
|
+
const res = await this.#post(input, false);
|
|
280
|
+
let data;
|
|
281
|
+
try {
|
|
282
|
+
data = await res.json();
|
|
283
|
+
} catch (e) {
|
|
284
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse LLM response: ${errorMessage$1(e)}`, e);
|
|
285
|
+
}
|
|
286
|
+
return this.#parseResponse(data);
|
|
287
|
+
}
|
|
288
|
+
/** SSE 流式:text_delta → text-delta;input_json_delta 按 block index 聚合 → tool-calls */
|
|
289
|
+
async *stream(input) {
|
|
290
|
+
const res = await this.#post(input, true);
|
|
291
|
+
if (!res.body) throw new WebSkillError("LLM_REQUEST_FAILED", "LLM streaming response has no body");
|
|
292
|
+
const toolCallsByIndex = /* @__PURE__ */ new Map();
|
|
293
|
+
const decoder = new TextDecoder();
|
|
294
|
+
const reader = res.body.getReader();
|
|
295
|
+
let buffer = "";
|
|
296
|
+
const handleFrame = function* (data) {
|
|
297
|
+
let chunk;
|
|
298
|
+
try {
|
|
299
|
+
chunk = JSON.parse(data);
|
|
300
|
+
} catch (e) {
|
|
301
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse LLM stream frame: ${errorMessage$1(e)}`, data);
|
|
302
|
+
}
|
|
303
|
+
const type = chunk["type"];
|
|
304
|
+
if (type === "error") throw new WebSkillError("LLM_REQUEST_FAILED", `LLM stream error: ${chunk["error"]?.message ?? data}`);
|
|
305
|
+
if (type === "content_block_start") {
|
|
306
|
+
const block = chunk["content_block"];
|
|
307
|
+
if (block?.["type"] === "tool_use") {
|
|
308
|
+
const index = typeof chunk["index"] === "number" ? chunk["index"] : 0;
|
|
309
|
+
toolCallsByIndex.set(index, {
|
|
310
|
+
id: typeof block["id"] === "string" ? block["id"] : "",
|
|
311
|
+
name: typeof block["name"] === "string" ? block["name"] : "",
|
|
312
|
+
arguments: ""
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (type === "content_block_delta") {
|
|
318
|
+
const index = typeof chunk["index"] === "number" ? chunk["index"] : 0;
|
|
319
|
+
const delta = chunk["delta"];
|
|
320
|
+
if (delta?.["type"] === "text_delta" && typeof delta["text"] === "string" && delta["text"] !== "") {
|
|
321
|
+
yield {
|
|
322
|
+
type: "text-delta",
|
|
323
|
+
delta: delta["text"]
|
|
324
|
+
};
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (delta?.["type"] === "input_json_delta" && typeof delta["partial_json"] === "string") {
|
|
328
|
+
const acc = toolCallsByIndex.get(index) ?? {
|
|
329
|
+
id: "",
|
|
330
|
+
name: "",
|
|
331
|
+
arguments: ""
|
|
332
|
+
};
|
|
333
|
+
acc.arguments += delta["partial_json"];
|
|
334
|
+
toolCallsByIndex.set(index, acc);
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
try {
|
|
340
|
+
for (;;) {
|
|
341
|
+
const { value, done: readerDone } = await reader.read();
|
|
342
|
+
if (readerDone) break;
|
|
343
|
+
buffer += decoder.decode(value, { stream: true });
|
|
344
|
+
buffer = buffer.replace(/\r\n/g, "\n");
|
|
345
|
+
let newline;
|
|
346
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
347
|
+
const line = buffer.slice(0, newline).trim();
|
|
348
|
+
buffer = buffer.slice(newline + 1);
|
|
349
|
+
if (line === "" || line.startsWith(":") || line.startsWith("event:")) continue;
|
|
350
|
+
if (!line.startsWith("data:")) continue;
|
|
351
|
+
yield* handleFrame(line.slice(5).trim());
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
} finally {
|
|
355
|
+
reader.releaseLock();
|
|
356
|
+
}
|
|
357
|
+
if (toolCallsByIndex.size > 0) yield {
|
|
358
|
+
type: "tool-calls",
|
|
359
|
+
toolCalls: [...toolCallsByIndex.entries()].sort(([a], [b]) => a - b).map(([index, acc]) => {
|
|
360
|
+
let args = {};
|
|
361
|
+
try {
|
|
362
|
+
args = JSON.parse(acc.arguments || "{}");
|
|
363
|
+
} catch {}
|
|
364
|
+
return {
|
|
365
|
+
id: acc.id || `call-${index}`,
|
|
366
|
+
name: acc.name,
|
|
367
|
+
arguments: args
|
|
368
|
+
};
|
|
369
|
+
})
|
|
370
|
+
};
|
|
371
|
+
yield { type: "done" };
|
|
372
|
+
}
|
|
373
|
+
async #post(input, stream) {
|
|
374
|
+
const { system, messages } = toAnthropicMessages(input.messages);
|
|
375
|
+
const body = {
|
|
376
|
+
model: input.model ?? this.#config.model,
|
|
377
|
+
max_tokens: this.#config.maxTokens ?? 4096,
|
|
378
|
+
messages
|
|
379
|
+
};
|
|
380
|
+
if (system !== void 0) body["system"] = system;
|
|
381
|
+
if (input.tools?.length) body["tools"] = toAnthropicTools(input.tools);
|
|
382
|
+
if (input.temperature !== void 0) body["temperature"] = input.temperature;
|
|
383
|
+
if (stream) body["stream"] = true;
|
|
384
|
+
let res;
|
|
385
|
+
try {
|
|
386
|
+
res = await this.#fetch(`${this.#baseUrl()}/v1/messages`, {
|
|
387
|
+
method: "POST",
|
|
388
|
+
headers: this.#headers(),
|
|
389
|
+
body: JSON.stringify(body),
|
|
390
|
+
signal: input.signal ?? (this.#config.requestTimeoutMs ? AbortSignal.timeout(this.#config.requestTimeoutMs) : null)
|
|
391
|
+
});
|
|
392
|
+
} catch (e) {
|
|
393
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `LLM request failed: ${errorMessage$1(e)}`, e);
|
|
394
|
+
}
|
|
395
|
+
if (!res.ok) {
|
|
396
|
+
const detail = await res.text().catch(() => "");
|
|
397
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `LLM request failed with HTTP ${res.status}${detail ? `: ${detail.slice(0, 500)}` : ""}`);
|
|
398
|
+
}
|
|
399
|
+
return res;
|
|
400
|
+
}
|
|
401
|
+
/** 轻量探测(GET /v1/models),集成测试据此决定 skip */
|
|
402
|
+
async checkAvailability() {
|
|
403
|
+
try {
|
|
404
|
+
return (await this.#fetch(`${this.#baseUrl()}/v1/models`, { headers: this.#headers() })).ok;
|
|
405
|
+
} catch {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
#parseResponse(data) {
|
|
410
|
+
try {
|
|
411
|
+
const blocks = data.content;
|
|
412
|
+
if (!Array.isArray(blocks)) throw new Error("response has no content blocks");
|
|
413
|
+
const text = blocks.filter((b) => b["type"] === "text").map((b) => String(b["text"] ?? "")).join("");
|
|
414
|
+
const toolCalls = blocks.filter((b) => b["type"] === "tool_use").map((b) => ({
|
|
415
|
+
id: typeof b["id"] === "string" ? b["id"] : "",
|
|
416
|
+
name: typeof b["name"] === "string" ? b["name"] : "",
|
|
417
|
+
arguments: typeof b["input"] === "object" && b["input"] !== null ? b["input"] : {}
|
|
418
|
+
}));
|
|
419
|
+
return {
|
|
420
|
+
content: text === "" ? void 0 : text,
|
|
421
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
422
|
+
raw: data
|
|
423
|
+
};
|
|
424
|
+
} catch (e) {
|
|
425
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to interpret LLM response: ${errorMessage$1(e)}`, data);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
const errorMessage = (e) => e instanceof Error ? e.message : String(e);
|
|
430
|
+
/** tool 结果文本包装为 functionResponse.response 对象 */
|
|
431
|
+
function responseObject(content) {
|
|
432
|
+
try {
|
|
433
|
+
const parsed = JSON.parse(content);
|
|
434
|
+
if (typeof parsed === "object" && parsed !== null) return parsed;
|
|
435
|
+
} catch {}
|
|
436
|
+
return { result: content };
|
|
437
|
+
}
|
|
438
|
+
/** LlmMessage 序列 → systemInstruction + contents(tool 结果合并进 user 消息 functionResponse parts) */
|
|
439
|
+
function toGenAiContents(messages) {
|
|
440
|
+
const system = messages.filter((m) => m.role === "system").map((m) => m.content).join("\n");
|
|
441
|
+
const nameByCallId = /* @__PURE__ */ new Map();
|
|
442
|
+
for (const msg of messages) if (msg.role === "assistant") for (const call of msg.toolCalls ?? []) nameByCallId.set(call.id, call.name);
|
|
443
|
+
const out = [];
|
|
444
|
+
for (const msg of messages) {
|
|
445
|
+
if (msg.role === "system") continue;
|
|
446
|
+
if (msg.role === "tool") {
|
|
447
|
+
const callId = msg.toolCallId ?? "";
|
|
448
|
+
const part = { functionResponse: {
|
|
449
|
+
name: nameByCallId.get(callId) ?? callId,
|
|
450
|
+
response: responseObject(msg.content)
|
|
451
|
+
} };
|
|
452
|
+
const last = out.at(-1);
|
|
453
|
+
if (last && last.role === "user") last.parts.push(part);
|
|
454
|
+
else out.push({
|
|
455
|
+
role: "user",
|
|
456
|
+
parts: [part]
|
|
457
|
+
});
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (msg.role === "assistant") {
|
|
461
|
+
const parts = [];
|
|
462
|
+
if (msg.content !== "") parts.push({ text: msg.content });
|
|
463
|
+
for (const call of msg.toolCalls ?? []) parts.push({ functionCall: {
|
|
464
|
+
name: call.name,
|
|
465
|
+
args: call.arguments
|
|
466
|
+
} });
|
|
467
|
+
out.push({
|
|
468
|
+
role: "model",
|
|
469
|
+
parts: parts.length > 0 ? parts : [{ text: "" }]
|
|
470
|
+
});
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
out.push({
|
|
474
|
+
role: "user",
|
|
475
|
+
parts: [{ text: msg.content }]
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return system === "" ? { contents: out } : {
|
|
479
|
+
systemInstruction: { parts: [{ text: system }] },
|
|
480
|
+
contents: out
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
const toGenAiTools = (tools) => [{ functionDeclarations: tools.map((tool) => ({
|
|
484
|
+
name: tool.name,
|
|
485
|
+
...tool.description ? { description: tool.description } : {},
|
|
486
|
+
parameters: tool.inputSchema
|
|
487
|
+
})) }];
|
|
488
|
+
/** Google GenAI(generateContent / streamGenerateContent)客户端(零依赖 fetch) */
|
|
489
|
+
var GoogleGenAiClient = class {
|
|
490
|
+
#config;
|
|
491
|
+
#fetch;
|
|
492
|
+
constructor(config) {
|
|
493
|
+
this.#config = config;
|
|
494
|
+
this.#fetch = config.fetchImpl ?? fetch;
|
|
495
|
+
}
|
|
496
|
+
#baseUrl() {
|
|
497
|
+
return (this.#config.baseUrl ?? "https://generativelanguage.googleapis.com").replace(/\/+$/, "");
|
|
498
|
+
}
|
|
499
|
+
async complete(input) {
|
|
500
|
+
const res = await this.#post(input, false);
|
|
501
|
+
let data;
|
|
502
|
+
try {
|
|
503
|
+
data = await res.json();
|
|
504
|
+
} catch (e) {
|
|
505
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse LLM response: ${errorMessage(e)}`, e);
|
|
506
|
+
}
|
|
507
|
+
const parts = this.#responseParts(data);
|
|
508
|
+
const text = parts.filter((p) => typeof p["text"] === "string").map((p) => String(p["text"])).join("");
|
|
509
|
+
const toolCalls = this.#functionCalls(parts);
|
|
510
|
+
return {
|
|
511
|
+
content: text === "" ? void 0 : text,
|
|
512
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
513
|
+
raw: data
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
/** SSE 流式:chunk.parts text → text-delta;functionCall 聚合 → tool-calls */
|
|
517
|
+
async *stream(input) {
|
|
518
|
+
const res = await this.#post(input, true);
|
|
519
|
+
if (!res.body) throw new WebSkillError("LLM_REQUEST_FAILED", "LLM streaming response has no body");
|
|
520
|
+
const toolCalls = [];
|
|
521
|
+
const decoder = new TextDecoder();
|
|
522
|
+
const reader = res.body.getReader();
|
|
523
|
+
let buffer = "";
|
|
524
|
+
const handleFrame = function* (data) {
|
|
525
|
+
let chunk;
|
|
526
|
+
try {
|
|
527
|
+
chunk = JSON.parse(data);
|
|
528
|
+
} catch (e) {
|
|
529
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse LLM stream frame: ${errorMessage(e)}`, data);
|
|
530
|
+
}
|
|
531
|
+
const parts = chunk.candidates?.[0]?.content?.parts;
|
|
532
|
+
for (const part of parts ?? []) {
|
|
533
|
+
if (typeof part["text"] === "string" && part["text"] !== "") {
|
|
534
|
+
yield {
|
|
535
|
+
type: "text-delta",
|
|
536
|
+
delta: part["text"]
|
|
537
|
+
};
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
const call = part["functionCall"];
|
|
541
|
+
if (call) toolCalls.push({
|
|
542
|
+
id: `call-${toolCalls.length}`,
|
|
543
|
+
name: call.name ?? "",
|
|
544
|
+
arguments: call.args ?? {}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
try {
|
|
549
|
+
for (;;) {
|
|
550
|
+
const { value, done: readerDone } = await reader.read();
|
|
551
|
+
if (readerDone) break;
|
|
552
|
+
buffer += decoder.decode(value, { stream: true });
|
|
553
|
+
buffer = buffer.replace(/\r\n/g, "\n");
|
|
554
|
+
let newline;
|
|
555
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
556
|
+
const line = buffer.slice(0, newline).trim();
|
|
557
|
+
buffer = buffer.slice(newline + 1);
|
|
558
|
+
if (line === "" || line.startsWith(":") || line.startsWith("event:")) continue;
|
|
559
|
+
if (!line.startsWith("data:")) continue;
|
|
560
|
+
yield* handleFrame(line.slice(5).trim());
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
} finally {
|
|
564
|
+
reader.releaseLock();
|
|
565
|
+
}
|
|
566
|
+
if (toolCalls.length > 0) yield {
|
|
567
|
+
type: "tool-calls",
|
|
568
|
+
toolCalls
|
|
569
|
+
};
|
|
570
|
+
yield { type: "done" };
|
|
571
|
+
}
|
|
572
|
+
async #post(input, stream) {
|
|
573
|
+
const model = input.model ?? this.#config.model;
|
|
574
|
+
const action = stream ? `:streamGenerateContent?alt=sse&key=${encodeURIComponent(this.#config.apiKey)}` : `:generateContent?key=${encodeURIComponent(this.#config.apiKey)}`;
|
|
575
|
+
const { systemInstruction, contents } = toGenAiContents(input.messages);
|
|
576
|
+
const body = { contents };
|
|
577
|
+
if (systemInstruction) body["systemInstruction"] = systemInstruction;
|
|
578
|
+
if (input.tools?.length) body["tools"] = toGenAiTools(input.tools);
|
|
579
|
+
if (input.temperature !== void 0) body["generationConfig"] = { temperature: input.temperature };
|
|
580
|
+
let res;
|
|
581
|
+
try {
|
|
582
|
+
res = await this.#fetch(`${this.#baseUrl()}/v1beta/models/${encodeURIComponent(model)}${action}`, {
|
|
583
|
+
method: "POST",
|
|
584
|
+
headers: { "content-type": "application/json" },
|
|
585
|
+
body: JSON.stringify(body),
|
|
586
|
+
signal: input.signal ?? (this.#config.requestTimeoutMs ? AbortSignal.timeout(this.#config.requestTimeoutMs) : null)
|
|
587
|
+
});
|
|
588
|
+
} catch (e) {
|
|
589
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `LLM request failed: ${errorMessage(e)}`, e);
|
|
590
|
+
}
|
|
591
|
+
if (!res.ok) {
|
|
592
|
+
const detail = await res.text().catch(() => "");
|
|
593
|
+
throw new WebSkillError("LLM_REQUEST_FAILED", `LLM request failed with HTTP ${res.status}${detail ? `: ${detail.slice(0, 500)}` : ""}`);
|
|
594
|
+
}
|
|
595
|
+
return res;
|
|
596
|
+
}
|
|
597
|
+
/** 轻量探测(GET /v1beta/models),集成测试据此决定 skip */
|
|
598
|
+
async checkAvailability() {
|
|
599
|
+
try {
|
|
600
|
+
return (await this.#fetch(`${this.#baseUrl()}/v1beta/models?key=${encodeURIComponent(this.#config.apiKey)}`)).ok;
|
|
601
|
+
} catch {
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
#responseParts(data) {
|
|
606
|
+
try {
|
|
607
|
+
const parts = data.candidates?.[0]?.content?.parts;
|
|
608
|
+
if (!Array.isArray(parts)) throw new Error("response has no candidates[0].content.parts");
|
|
609
|
+
return parts;
|
|
199
610
|
} catch (e) {
|
|
200
611
|
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to interpret LLM response: ${errorMessage(e)}`, data);
|
|
201
612
|
}
|
|
202
613
|
}
|
|
614
|
+
#functionCalls(parts) {
|
|
615
|
+
const out = [];
|
|
616
|
+
for (const part of parts) {
|
|
617
|
+
const call = part["functionCall"];
|
|
618
|
+
if (call) out.push({
|
|
619
|
+
id: `call-${out.length}`,
|
|
620
|
+
name: call.name ?? "",
|
|
621
|
+
arguments: call.args ?? {}
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
return out;
|
|
625
|
+
}
|
|
203
626
|
};
|
|
204
627
|
function toVercelToolSpecs(tools) {
|
|
205
628
|
const out = {};
|
|
@@ -1154,6 +1577,7 @@ var AgentLoop = class {
|
|
|
1154
1577
|
}
|
|
1155
1578
|
/** 经外部技能提供者读取技能文档/资源;skillKey 为技能名或 mcp:// URI */
|
|
1156
1579
|
async #handleReadExternalSkill(skillKey, pathArg, state) {
|
|
1580
|
+
const failures = [];
|
|
1157
1581
|
for (const provider of this.#deps.skillProviders ?? []) try {
|
|
1158
1582
|
let name = skillKey;
|
|
1159
1583
|
if (skillKey.startsWith("mcp://")) {
|
|
@@ -1187,10 +1611,12 @@ var AgentLoop = class {
|
|
|
1187
1611
|
text: await provider.readFile(name, pathArg)
|
|
1188
1612
|
}]
|
|
1189
1613
|
};
|
|
1190
|
-
} catch {
|
|
1614
|
+
} catch (e) {
|
|
1615
|
+
failures.push(messageOf$1(e));
|
|
1191
1616
|
continue;
|
|
1192
1617
|
}
|
|
1193
|
-
|
|
1618
|
+
const detail = failures.length > 0 ? ` (provider errors: ${failures.join("; ")})` : "";
|
|
1619
|
+
return toolError("SKILL_NOT_FOUND", `Skill not found via external providers: ${skillKey}${detail}`);
|
|
1194
1620
|
}
|
|
1195
1621
|
/** 首次读到 SKILL.md 时激活技能:加载其 scripts 工具定义,供后续轮次使用;dependencies 级联激活(via 记录来源) */
|
|
1196
1622
|
async #activateSkill(skillName, state, via, skillMdText) {
|
|
@@ -1212,7 +1638,9 @@ var AgentLoop = class {
|
|
|
1212
1638
|
const rawAllowed = metadata["allowed-tools"];
|
|
1213
1639
|
if (rawAllowed !== void 0) if (Array.isArray(rawAllowed)) allowedTools = rawAllowed.filter((e) => typeof e === "string");
|
|
1214
1640
|
else state.trace.record("run.warning", { message: `Skill "${skillName}" has a non-array "allowed-tools" metadata entry; ignored` });
|
|
1215
|
-
} catch {
|
|
1641
|
+
} catch (e) {
|
|
1642
|
+
state.trace.record("run.warning", { message: `Failed to read or parse SKILL.md of skill "${skillName}": ${messageOf$1(e)}` });
|
|
1643
|
+
}
|
|
1216
1644
|
const executor = this.#deps.executor;
|
|
1217
1645
|
const loaded = [];
|
|
1218
1646
|
if (executor) {
|
|
@@ -1231,7 +1659,9 @@ var AgentLoop = class {
|
|
|
1231
1659
|
await this.#enrichDefinition(root, match[1], def, state);
|
|
1232
1660
|
state.activatedTools.set(def.name, def);
|
|
1233
1661
|
loaded.push(def.name);
|
|
1234
|
-
} catch {
|
|
1662
|
+
} catch (e) {
|
|
1663
|
+
state.trace.record("run.warning", { message: `Failed to load definition of script "${match[1]}" for skill "${skillName}": ${messageOf$1(e)}` });
|
|
1664
|
+
}
|
|
1235
1665
|
}
|
|
1236
1666
|
}
|
|
1237
1667
|
let note = loaded.length ? `\n\nActivated tools: ${loaded.join(", ")}` : "";
|
|
@@ -1501,10 +1931,12 @@ var WebSkillRuntime = class {
|
|
|
1501
1931
|
if (!this.#catalogCache) await this.discover();
|
|
1502
1932
|
const cache = this.#catalogCache;
|
|
1503
1933
|
if (!cache) throw new Error("discover() did not populate the catalog cache");
|
|
1934
|
+
const providerFailures = [];
|
|
1504
1935
|
const providerEntries = (await Promise.all((this.#deps.skillProviders ?? []).map(async (p) => {
|
|
1505
1936
|
try {
|
|
1506
1937
|
return await p.listSkills();
|
|
1507
|
-
} catch {
|
|
1938
|
+
} catch (e) {
|
|
1939
|
+
providerFailures.push(e instanceof Error ? e.message : String(e));
|
|
1508
1940
|
return [];
|
|
1509
1941
|
}
|
|
1510
1942
|
}))).flat();
|
|
@@ -1534,6 +1966,13 @@ var WebSkillRuntime = class {
|
|
|
1534
1966
|
userPrompt,
|
|
1535
1967
|
route
|
|
1536
1968
|
});
|
|
1969
|
+
for (const failure of providerFailures) result.run.trace.push({
|
|
1970
|
+
id: `evt-provider-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
1971
|
+
runId: result.run.id,
|
|
1972
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1973
|
+
type: "run.warning",
|
|
1974
|
+
message: `Skill provider listSkills() failed: ${failure}`
|
|
1975
|
+
});
|
|
1537
1976
|
if (this.#deps.onSkillMiss && result.run.activeSkillNames.length === 0) this.#deps.onSkillMiss({
|
|
1538
1977
|
prompt: userPrompt,
|
|
1539
1978
|
run: result.run
|
|
@@ -1856,6 +2295,7 @@ var FsArtifactStore = class {
|
|
|
1856
2295
|
});
|
|
1857
2296
|
}
|
|
1858
2297
|
async listArtifacts(runId) {
|
|
2298
|
+
assertSafePathSegment(runId, "runId");
|
|
1859
2299
|
const indexPath = `${this.#root}/${runId}/${INDEX_FILE}`;
|
|
1860
2300
|
if (!await this.#fs.exists(indexPath)) return [];
|
|
1861
2301
|
const raw = await this.#fs.readText(indexPath);
|
|
@@ -1867,6 +2307,7 @@ var FsArtifactStore = class {
|
|
|
1867
2307
|
}
|
|
1868
2308
|
}
|
|
1869
2309
|
#artifactPath(runId, artifactPath) {
|
|
2310
|
+
assertSafePathSegment(runId, "runId");
|
|
1870
2311
|
return resolveInsideRoot(`${this.#root}/${runId}`, artifactPath);
|
|
1871
2312
|
}
|
|
1872
2313
|
async #persist(input) {
|
|
@@ -2017,4 +2458,4 @@ var CapabilityApproval = class {
|
|
|
2017
2458
|
};
|
|
2018
2459
|
|
|
2019
2460
|
//#endregion
|
|
2020
|
-
export {
|
|
2461
|
+
export { mergeCatalogEntries as A, buildRenderResult as C, fromVercelResult as D, extractChartSpec as E, schemaToForm as F, toLlmToolSpec as I, toVercelToolSpecs as L, normalizeToolContent as M, parseBridgeRequest as N, fromVercelStreamPart as O, resolveToolName as P, bridgeError as S, createWebSkillApi as T, READ_SKILL_FILE_TOOL as _, AnthropicClient as a, TraceRecorder as b, FsArtifactStore as c, FullDisclosureRouter as d, GoogleGenAiClient as f, READ_SKILL_FILE_INPUT_SCHEMA as g, ProgressiveRouter as h, AgentLoop as i, networkUrlHost as j, isNetworkAllowed 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, createScriptContext as w, WebSkillRuntime as x, RUN_SNAPSHOT_SCHEMA_VERSION as y };
|
|
@@ -174,8 +174,20 @@ function normalizePath(path) {
|
|
|
174
174
|
return path.replace(/\\/g, "/").split("/").filter((s) => s !== "" && s !== ".").join("/");
|
|
175
175
|
}
|
|
176
176
|
/**
|
|
177
|
+
* 外部标识(runId、candidateId、skillName、versionId 等)作为单一路径段使用前的统一校验:
|
|
178
|
+
* 拒绝空串、`.`、`..`、含 `/` 或 `\`、含 `:`(Windows 盘符/ADS)。
|
|
179
|
+
* 违规抛 FS_PATH_OUTSIDE_ROOT(kind 用于错误消息定位,如 "runId")。
|
|
180
|
+
*/
|
|
181
|
+
function assertSafePathSegment(segment, kind) {
|
|
182
|
+
if (segment === "" || segment === "." || segment === ".." || segment.includes("/") || segment.includes("\\") || segment.includes(":")) throw new WebSkillError("FS_PATH_OUTSIDE_ROOT", `Invalid ${kind} (must be a single safe path segment): ${JSON.stringify(segment)}`);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
177
185
|
* 将相对路径安全地解析到 root 之内。
|
|
178
186
|
* 拒绝空路径、绝对路径(`/`、`\\`、`C:\` 形式)与任何 `..` 段。
|
|
187
|
+
*
|
|
188
|
+
* 注意:本函数仅做**词法**校验,不解析符号链接——root 内经符号链接指向
|
|
189
|
+
* 外部的目标无法被词法检查拦截(realpath 防护见 NodeFS root 模式与
|
|
190
|
+
* 安装管线的 lstat 符号链接扫描)。
|
|
179
191
|
*/
|
|
180
192
|
function resolveInsideRoot(root, relativePath) {
|
|
181
193
|
const fail = (reason) => {
|
|
@@ -587,4 +599,4 @@ const xmlRenderer = {
|
|
|
587
599
|
};
|
|
588
600
|
|
|
589
601
|
//#endregion
|
|
590
|
-
export {
|
|
602
|
+
export { renderAvailableSkillsXml as C, verifyManifest as D, validateSkills as E, xmlRenderer as O, parseSkillPackManifest as S, resolveInsideRoot as T, exportSkills as _, SKILL_NAME_PATTERN as a, normalizePath as b, SkillReader as c, buildCatalog as d, buildManifest as f, escapeXml as g, computeDigest as h, SKILL_NAME_MAX_LENGTH as i, WebSkillError as l, checkSkillRules as m, SKILLS_LOCKFILE as n, SKILL_PACK_FILE as o, checkDependencyCycles as p, SKILL_MANIFEST_FILE as r, SkillDiscovery as s, MemoryFS as t, assertSafePathSegment as u, isValidSkillName as v, renderCatalogJson as w, parseSkillMarkdown as x, jsonRenderer as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as WebSkillError } from "./dist-
|
|
1
|
+
import { l as WebSkillError } from "./dist-DvoBsChX.js";
|
|
2
2
|
|
|
3
3
|
//#region ../ui/dist/index.js
|
|
4
4
|
/** 提交值按请求类型归形(WebFormBridge 与框架组件库共享单一来源) */
|
|
@@ -1040,8 +1040,7 @@ var LitRendererBridge = class {
|
|
|
1040
1040
|
values: formValues
|
|
1041
1041
|
}
|
|
1042
1042
|
});
|
|
1043
|
-
|
|
1044
|
-
resolve(response);
|
|
1043
|
+
resolve(this.#decodeValue(input, response));
|
|
1045
1044
|
}, { version: "v0.9.1" });
|
|
1046
1045
|
processor.onSurfaceCreated((surface) => {
|
|
1047
1046
|
const el = this.#doc.createElement("a2ui-surface");
|
|
@@ -1052,6 +1051,50 @@ var LitRendererBridge = class {
|
|
|
1052
1051
|
processor.processMessages(messages);
|
|
1053
1052
|
});
|
|
1054
1053
|
}
|
|
1054
|
+
/**
|
|
1055
|
+
* 按 InteractionRequest.type 解码提交值(绑定模型原样回传的是表单对象):
|
|
1056
|
+
* confirm 仅 confirmed===true 批准;select 还原原始 option 值(非字符串值经 JSON 编码比对);
|
|
1057
|
+
* form 的 number 字段转 number;ask 取 answer;authorize 提交即批准。
|
|
1058
|
+
*/
|
|
1059
|
+
#decodeValue(input, response) {
|
|
1060
|
+
if (response.cancelled) return response;
|
|
1061
|
+
const values = response.value;
|
|
1062
|
+
switch (input.type) {
|
|
1063
|
+
case "confirm": return {
|
|
1064
|
+
...response,
|
|
1065
|
+
value: values?.confirmed === true
|
|
1066
|
+
};
|
|
1067
|
+
case "ask": return {
|
|
1068
|
+
...response,
|
|
1069
|
+
value: values?.answer
|
|
1070
|
+
};
|
|
1071
|
+
case "select": {
|
|
1072
|
+
const raw = values?.selected;
|
|
1073
|
+
const option = input.options.find((o) => o.value === raw || JSON.stringify(o.value) === raw);
|
|
1074
|
+
return {
|
|
1075
|
+
...response,
|
|
1076
|
+
value: option ? option.value : raw
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
case "form": {
|
|
1080
|
+
if (typeof values !== "object" || values === null) return response;
|
|
1081
|
+
const out = { ...values };
|
|
1082
|
+
for (const field of input.fields) if (field.type === "number" && out[field.name] !== void 0) {
|
|
1083
|
+
const n = Number(out[field.name]);
|
|
1084
|
+
if (!Number.isNaN(n)) out[field.name] = n;
|
|
1085
|
+
}
|
|
1086
|
+
return {
|
|
1087
|
+
...response,
|
|
1088
|
+
value: out
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
case "authorize": return {
|
|
1092
|
+
...response,
|
|
1093
|
+
value: true
|
|
1094
|
+
};
|
|
1095
|
+
default: return response;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1055
1098
|
};
|
|
1056
1099
|
|
|
1057
1100
|
//#endregion
|
package/dist/governance.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { L as SkillManifest, N as SkillDocument, S as FileSystemProvider, c as LlmClient, j as SkillCatalogEntry, u as LlmMessage, v as UiBridge } from "./types-CgNC-oQu-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { L as SkillManifest, N as SkillDocument, S as FileSystemProvider, c as LlmClient, j as SkillCatalogEntry, u as LlmMessage, v as UiBridge } from "./types-CgNC-oQu-DYrxhD3z.js";
|
|
2
|
+
import { ct as WebSkillRuntime, q as RuntimeRun } from "./index-BSS3EWY-.js";
|
|
3
|
+
import { f as SkillManager } from "./index-npFMt2Zw.js";
|
|
4
4
|
//#region ../governance/dist/index.d.ts
|
|
5
5
|
//#region src/types.d.ts
|
|
6
6
|
type CandidateStatus = 'draft' | 'pending-review' | 'approved' | 'published' | 'rejected';
|
|
@@ -311,11 +311,14 @@ interface EvaluationReport {
|
|
|
311
311
|
}
|
|
312
312
|
//#endregion
|
|
313
313
|
//#region src/evaluation/evaluationRunner.d.ts
|
|
314
|
-
/** 批量评估:顺序执行(单任务异常不中断批);expected
|
|
314
|
+
/** 批量评估:顺序执行(单任务异常不中断批);expected 参与判定;结构化报告。
|
|
315
|
+
* 注意:评估会真实执行技能脚本(试用路径)——必须显式 opt-in(allowExecution: true),
|
|
316
|
+
* 且沙箱执行器是能力面收敛而非安全边界,禁止对不可信候选技能直接跑评估。 */
|
|
315
317
|
declare class EvaluationRunner {
|
|
316
318
|
#private;
|
|
317
319
|
constructor(deps: {
|
|
318
320
|
runtime: WebSkillRuntime;
|
|
321
|
+
allowExecution?: boolean;
|
|
319
322
|
now?: () => string;
|
|
320
323
|
createId?: () => string;
|
|
321
324
|
});
|