@webskill/sdk 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +3 -3
- package/dist/{dist-Cc3Mqi3_.js → dist-Cm_j6Jol.js} +41 -8
- package/dist/{memoryArtifactStore-C9lFVqPF-U8nXvqL9.js → dist-DS1sfgHa.js} +1 -48
- package/dist/{dist-D5fsiETF.js → dist-DZobLFh6.js} +472 -25
- package/dist/dist-DxGyAznR.js +1058 -0
- package/dist/governance.d.ts +2 -2
- package/dist/governance.js +2 -2
- package/dist/{index-Dc4X2N54.d.ts → index-CPuwsnmB.d.ts} +12 -3
- package/dist/{index-oG5Nzgb9.d.ts → index-CqMuvcb2.d.ts} +49 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +29 -2
- package/dist/mcp.js +61 -14
- package/dist/memoryArtifactStore-C9lFVqPF-yFz6yJj0.js +48 -0
- package/dist/node.d.ts +3 -3
- package/dist/node.js +4 -4
- package/dist/{testing-pn3NhXSV.js → testing-CbM6rJ-E.js} +1 -1
- package/dist/testing.js +2 -2
- package/dist/ui-react.js +1 -1
- package/dist/ui-vue.js +1 -1
- package/dist/ui.d.ts +2 -11
- package/dist/ui.js +2 -2
- package/package.json +1 -1
- package/dist/dist-Dv4a1Jkm.js +0 -15628
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as renderAvailableSkillsXml, T as validateSkills, b as parseSkillMarkdown, c as SkillReader, l as WebSkillError, s as SkillDiscovery, u as buildCatalog, w as resolveInsideRoot } from "./dist-DS1sfgHa.js";
|
|
2
|
+
import { t as MemoryArtifactStore } from "./memoryArtifactStore-C9lFVqPF-yFz6yJj0.js";
|
|
2
3
|
|
|
3
4
|
//#region ../runtime/dist/index.js
|
|
4
5
|
const toOpenAiMessage = (msg) => {
|
|
@@ -32,7 +33,7 @@ const toOpenAiTools = (tools) => tools.map((tool) => ({
|
|
|
32
33
|
parameters: tool.inputSchema
|
|
33
34
|
}
|
|
34
35
|
}));
|
|
35
|
-
const errorMessage = (e) => e instanceof Error ? e.message : String(e);
|
|
36
|
+
const errorMessage$2 = (e) => e instanceof Error ? e.message : String(e);
|
|
36
37
|
/** OpenAI chat completions 协议客户端(兼容端点通用) */
|
|
37
38
|
var OpenAiCompatibleClient = class {
|
|
38
39
|
#config;
|
|
@@ -56,7 +57,7 @@ var OpenAiCompatibleClient = class {
|
|
|
56
57
|
try {
|
|
57
58
|
data = await res.json();
|
|
58
59
|
} catch (e) {
|
|
59
|
-
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);
|
|
60
61
|
}
|
|
61
62
|
return this.#parseResponse(data);
|
|
62
63
|
}
|
|
@@ -78,7 +79,7 @@ var OpenAiCompatibleClient = class {
|
|
|
78
79
|
try {
|
|
79
80
|
chunk = JSON.parse(data);
|
|
80
81
|
} catch (e) {
|
|
81
|
-
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);
|
|
82
83
|
}
|
|
83
84
|
const delta = chunk.choices?.[0]?.delta;
|
|
84
85
|
if (!delta) return;
|
|
@@ -160,7 +161,7 @@ var OpenAiCompatibleClient = class {
|
|
|
160
161
|
signal: input.signal ?? (this.#config.requestTimeoutMs ? AbortSignal.timeout(this.#config.requestTimeoutMs) : null)
|
|
161
162
|
});
|
|
162
163
|
} catch (e) {
|
|
163
|
-
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);
|
|
164
165
|
}
|
|
165
166
|
if (!res.ok) {
|
|
166
167
|
const detail = await res.text().catch(() => "");
|
|
@@ -195,10 +196,433 @@ var OpenAiCompatibleClient = class {
|
|
|
195
196
|
toolCalls: toolCalls?.length ? toolCalls : void 0,
|
|
196
197
|
raw: data
|
|
197
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;
|
|
198
610
|
} catch (e) {
|
|
199
611
|
throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to interpret LLM response: ${errorMessage(e)}`, data);
|
|
200
612
|
}
|
|
201
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
|
+
}
|
|
202
626
|
};
|
|
203
627
|
function toVercelToolSpecs(tools) {
|
|
204
628
|
const out = {};
|
|
@@ -421,7 +845,7 @@ const ASK_USER_TOOL = {
|
|
|
421
845
|
* 不暴露 fs 本体(沙箱语义,对齐 deferred-items D1)。
|
|
422
846
|
*/
|
|
423
847
|
function createScriptContext(deps) {
|
|
424
|
-
const { fs, artifactStore, skillName, skillRoot, runId, confirm, onWarning } = deps;
|
|
848
|
+
const { fs, artifactStore, skillName, skillRoot, runId, confirm, onWarning, onArtifactCreated } = deps;
|
|
425
849
|
return {
|
|
426
850
|
skillName,
|
|
427
851
|
runId,
|
|
@@ -429,17 +853,19 @@ function createScriptContext(deps) {
|
|
|
429
853
|
return fs.readText(resolveInsideRoot(skillRoot, `references/${relativePath}`));
|
|
430
854
|
},
|
|
431
855
|
async writeArtifact(path, content, options) {
|
|
432
|
-
|
|
856
|
+
const artifact = typeof content === "string" ? await artifactStore.createTextArtifact({
|
|
433
857
|
runId,
|
|
434
858
|
path,
|
|
435
859
|
content,
|
|
436
860
|
mimeType: options?.mimeType
|
|
437
|
-
}) : artifactStore.createBinaryArtifact({
|
|
861
|
+
}) : await artifactStore.createBinaryArtifact({
|
|
438
862
|
runId,
|
|
439
863
|
path,
|
|
440
864
|
content,
|
|
441
865
|
mimeType: options?.mimeType
|
|
442
866
|
});
|
|
867
|
+
onArtifactCreated?.(artifact);
|
|
868
|
+
return artifact;
|
|
443
869
|
},
|
|
444
870
|
...confirm ? { confirm } : {},
|
|
445
871
|
...onWarning ? { onWarning } : {}
|
|
@@ -1136,7 +1562,7 @@ var AgentLoop = class {
|
|
|
1136
1562
|
try {
|
|
1137
1563
|
const text = await state.reader.readSkillFile(skillName, pathArg);
|
|
1138
1564
|
let note = "";
|
|
1139
|
-
if (pathArg === "SKILL.md" && !state.activated.has(skillName)) note = await this.#activateSkill(skillName, state);
|
|
1565
|
+
if (pathArg === "SKILL.md" && !state.activated.has(skillName)) note = await this.#activateSkill(skillName, state, void 0, text);
|
|
1140
1566
|
return {
|
|
1141
1567
|
ok: true,
|
|
1142
1568
|
content: [{
|
|
@@ -1190,7 +1616,7 @@ var AgentLoop = class {
|
|
|
1190
1616
|
return toolError("SKILL_NOT_FOUND", `Skill not found via external providers: ${skillKey}`);
|
|
1191
1617
|
}
|
|
1192
1618
|
/** 首次读到 SKILL.md 时激活技能:加载其 scripts 工具定义,供后续轮次使用;dependencies 级联激活(via 记录来源) */
|
|
1193
|
-
async #activateSkill(skillName, state, via) {
|
|
1619
|
+
async #activateSkill(skillName, state, via, skillMdText) {
|
|
1194
1620
|
state.activated.add(skillName);
|
|
1195
1621
|
state.trace.record("skill.activated", { data: {
|
|
1196
1622
|
skillName,
|
|
@@ -1203,11 +1629,11 @@ var AgentLoop = class {
|
|
|
1203
1629
|
let allowedTools;
|
|
1204
1630
|
let dependencies = [];
|
|
1205
1631
|
try {
|
|
1206
|
-
const { metadata } = parseSkillMarkdown(await this.#deps.fs.readText(`${root}/SKILL.md`));
|
|
1632
|
+
const { metadata } = parseSkillMarkdown(skillMdText ?? await this.#deps.fs.readText(`${root}/SKILL.md`));
|
|
1207
1633
|
const rawDeps = metadata["dependencies"];
|
|
1208
1634
|
if (Array.isArray(rawDeps)) dependencies = rawDeps.filter((d) => typeof d === "string");
|
|
1209
|
-
const
|
|
1210
|
-
if (
|
|
1635
|
+
const rawAllowed = metadata["allowed-tools"];
|
|
1636
|
+
if (rawAllowed !== void 0) if (Array.isArray(rawAllowed)) allowedTools = rawAllowed.filter((e) => typeof e === "string");
|
|
1211
1637
|
else state.trace.record("run.warning", { message: `Skill "${skillName}" has a non-array "allowed-tools" metadata entry; ignored` });
|
|
1212
1638
|
} catch {}
|
|
1213
1639
|
const executor = this.#deps.executor;
|
|
@@ -1290,6 +1716,7 @@ var AgentLoop = class {
|
|
|
1290
1716
|
if (e instanceof BridgeRequestError) return toolError("UI_UNAVAILABLE", `Parameter form failed: ${e.message}`);
|
|
1291
1717
|
throw e;
|
|
1292
1718
|
}
|
|
1719
|
+
const createdIds = /* @__PURE__ */ new Set();
|
|
1293
1720
|
const context = createScriptContext({
|
|
1294
1721
|
fs: this.#deps.fs,
|
|
1295
1722
|
artifactStore: this.#deps.artifactStore,
|
|
@@ -1297,10 +1724,10 @@ var AgentLoop = class {
|
|
|
1297
1724
|
skillRoot: root,
|
|
1298
1725
|
runId: state.runId,
|
|
1299
1726
|
confirm: (message) => this.#confirm(message, state),
|
|
1300
|
-
onWarning: (message) => state.trace.record("run.warning", { message })
|
|
1727
|
+
onWarning: (message) => state.trace.record("run.warning", { message }),
|
|
1728
|
+
onArtifactCreated: (artifact) => createdIds.add(artifact.id)
|
|
1301
1729
|
});
|
|
1302
1730
|
try {
|
|
1303
|
-
const before = new Set((await this.#deps.artifactStore.listArtifacts(state.runId)).map((a) => a.id));
|
|
1304
1731
|
const result = await this.#deps.executor.execute({
|
|
1305
1732
|
skillRoot: root,
|
|
1306
1733
|
scriptName,
|
|
@@ -1308,8 +1735,10 @@ var AgentLoop = class {
|
|
|
1308
1735
|
context,
|
|
1309
1736
|
timeoutMs: state.toolTimeoutMs
|
|
1310
1737
|
});
|
|
1311
|
-
|
|
1312
|
-
|
|
1738
|
+
if (createdIds.size > 0) {
|
|
1739
|
+
const fresh = (await this.#deps.artifactStore.listArtifacts(state.runId)).filter((a) => createdIds.has(a.id));
|
|
1740
|
+
if (fresh.length > 0) result.artifacts = [...result.artifacts ?? [], ...fresh];
|
|
1741
|
+
}
|
|
1313
1742
|
await this.#bumpSkillStat(skillName, result.ok ? "successes" : "failures", state);
|
|
1314
1743
|
return result;
|
|
1315
1744
|
} catch (e) {
|
|
@@ -1623,6 +2052,16 @@ var WebSkillRuntime = class {
|
|
|
1623
2052
|
function createWebSkillApi(deps) {
|
|
1624
2053
|
const { fs, roots } = deps;
|
|
1625
2054
|
let runtime;
|
|
2055
|
+
const discoveries = /* @__PURE__ */ new Map();
|
|
2056
|
+
const discoveryFor = (paths) => {
|
|
2057
|
+
const key = paths.join("");
|
|
2058
|
+
let discovery = discoveries.get(key);
|
|
2059
|
+
if (!discovery) {
|
|
2060
|
+
discovery = new SkillDiscovery(fs, paths);
|
|
2061
|
+
discoveries.set(key, discovery);
|
|
2062
|
+
}
|
|
2063
|
+
return discovery;
|
|
2064
|
+
};
|
|
1626
2065
|
const lazyRuntime = () => {
|
|
1627
2066
|
runtime ??= new WebSkillRuntime({
|
|
1628
2067
|
fs,
|
|
@@ -1640,11 +2079,11 @@ function createWebSkillApi(deps) {
|
|
|
1640
2079
|
};
|
|
1641
2080
|
return {
|
|
1642
2081
|
async discover(path) {
|
|
1643
|
-
const { entries } = await
|
|
2082
|
+
const { entries } = await discoveryFor([path]).discover();
|
|
1644
2083
|
return buildCatalog(entries);
|
|
1645
2084
|
},
|
|
1646
2085
|
async read(name) {
|
|
1647
|
-
const { entries } = await
|
|
2086
|
+
const { entries } = await discoveryFor(roots).discover();
|
|
1648
2087
|
const entry = entries.find((e) => e.name === name);
|
|
1649
2088
|
if (!entry) throw new WebSkillError("SKILL_NOT_FOUND", `Unknown skill: ${name}`);
|
|
1650
2089
|
const skillMdPath = `${entry.root}/SKILL.md`;
|
|
@@ -1679,13 +2118,21 @@ function createWebSkillApi(deps) {
|
|
|
1679
2118
|
}
|
|
1680
2119
|
};
|
|
1681
2120
|
}
|
|
1682
|
-
/** install URL 分派:http(s)….zip → http;….git → git(无 git 能力的 manager 自行 TOOL_UNSUPPORTED
|
|
2121
|
+
/** install URL 分派:http(s)….zip → http;….git → git(无 git 能力的 manager 自行 TOOL_UNSUPPORTED);
|
|
2122
|
+
* 其余 http(s) URL → INSTALL_FAILED(不静默当 local);非 URL → local */
|
|
1683
2123
|
function sourceFromUrl(url) {
|
|
1684
2124
|
const bare = url.split("?")[0] ?? url;
|
|
1685
|
-
if (/^https?:\/\//.test(url)
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
2125
|
+
if (/^https?:\/\//.test(url)) {
|
|
2126
|
+
if (bare.endsWith(".zip")) return {
|
|
2127
|
+
type: "http",
|
|
2128
|
+
url
|
|
2129
|
+
};
|
|
2130
|
+
if (bare.endsWith(".git")) return {
|
|
2131
|
+
type: "git",
|
|
2132
|
+
url
|
|
2133
|
+
};
|
|
2134
|
+
throw new WebSkillError("INSTALL_FAILED", `Unsupported install URL "${url}" (supported: http(s) URLs ending in .zip or .git, or local paths)`);
|
|
2135
|
+
}
|
|
1689
2136
|
if (bare.endsWith(".git")) return {
|
|
1690
2137
|
type: "git",
|
|
1691
2138
|
url
|
|
@@ -1993,4 +2440,4 @@ var CapabilityApproval = class {
|
|
|
1993
2440
|
};
|
|
1994
2441
|
|
|
1995
2442
|
//#endregion
|
|
1996
|
-
export {
|
|
2443
|
+
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 };
|