@webskill/sdk 0.5.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.
Files changed (36) hide show
  1. package/dist/agent.d.ts +2 -2
  2. package/dist/agent.js +274 -42
  3. package/dist/browser.d.ts +127 -5
  4. package/dist/browser.js +611 -17
  5. package/dist/{catalogComponents-DV7cPpUm-C77AEEx9.js → catalogComponents-Dr5dFMAb-DKH_7VPI.js} +848 -974
  6. package/dist/{dist-6C03DShK.js → dist-D0qW6e40.js} +1213 -210
  7. package/dist/{dist-bewtXYlO.js → dist-DnYG2-eY.js} +53 -14
  8. package/dist/{env--jJB-TSX-04klhTYi.js → env-8cY40DXB-CGnEVZby.js} +7 -6
  9. package/dist/{env-BPUBZCwJ-4jat_SVG.d.ts → env-AK3cSMEA-Dli6QU5E.d.ts} +4 -3
  10. package/dist/eventTypes-DjIQpt8Y-Bj3vghj4.js +32 -0
  11. package/dist/governance.d.ts +78 -11
  12. package/dist/governance.js +173 -50
  13. package/dist/{index-D_7ZZjkl.d.ts → index-Ba3xFtfz.d.ts} +222 -8
  14. package/dist/{index-Bsqg4ftU.d.ts → index-BwsK9lGk.d.ts} +13 -7
  15. package/dist/{index-vBz_FC9w.d.ts → index-DkbABR43.d.ts} +336 -52
  16. package/dist/index.d.ts +3 -3
  17. package/dist/index.js +3 -3
  18. package/dist/mcp.d.ts +159 -11
  19. package/dist/mcp.js +276 -45
  20. package/dist/{memoryArtifactStore-BtOeB_hm-tj3fC5ip.js → memoryArtifactStore-52Zn9npI-BMPYwvoy.js} +10 -2
  21. package/dist/node.d.ts +4 -4
  22. package/dist/node.js +11 -3
  23. package/dist/{openUiLibrary-W3Ce896k-ClFTRZFs.js → openUiLibrary-Bdrji9qK-D2LxmM-a.js} +3 -3
  24. package/dist/{skillVersionStore-BzLbzFOL-CxwIewHJ.d.ts → skillVersionStore-Bl-ElD45-CWPvGvoq.d.ts} +8 -2
  25. package/dist/{testing-DDCJWvgA.js → testing-CYTFqkDm.js} +1 -1
  26. package/dist/testing.d.ts +2 -2
  27. package/dist/testing.js +3 -3
  28. package/dist/{types-D_hoCri8-BnNPiZCi.d.ts → types-CcxRLdJG-DCXyw1US.d.ts} +62 -10
  29. package/dist/ui-react.d.ts +5 -3
  30. package/dist/ui-react.js +72 -47
  31. package/dist/ui-vue.d.ts +1 -1
  32. package/dist/ui-vue.js +13 -8
  33. package/dist/ui.d.ts +3 -3
  34. package/dist/ui.js +2 -2
  35. package/dist/{webskillLitCatalog-_mugzRHx-DiuJpCuf.js → webskillLitCatalog-_mugzRHx-B_54vxum.js} +1 -1
  36. package/package.json +1 -1
package/dist/mcp.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { O as messageOf, h as assertRemoteUrlAllowed, m as WebSkillError } from "./dist-8oQRa8Xz.js";
2
- import { U as mergeCatalogEntries, q as normalizeToolContent } from "./dist-6C03DShK.js";
2
+ import { nt as mergeCatalogEntries, st as normalizeToolContent } from "./dist-D0qW6e40.js";
3
3
 
4
4
  //#region ../mcp/dist/index.js
5
5
  /**
@@ -182,6 +182,8 @@ const failure$1 = (code, message) => ({
182
182
  message
183
183
  }
184
184
  });
185
+ /** 需要重新授权不是「端点不可用」:压平成后者,模型和界面都会去查网络(0.7.0 FR-22.3) */
186
+ const endpointFailure = (e, message) => failure$1(e instanceof WebSkillError && e.code.startsWith("MCP_OAUTH_") ? e.code : "MCP_ENDPOINT_UNAVAILABLE", message);
185
187
  /**
186
188
  * endpoint:toolName 调用 + 工具清单缓存(TTL 默认 1s + registry 版本号双失效)。
187
189
  */
@@ -204,7 +206,7 @@ var McpToolResolver = class {
204
206
  try {
205
207
  names = await this.#toolNames(endpoint, client);
206
208
  } catch (e) {
207
- return failure$1("MCP_ENDPOINT_UNAVAILABLE", `Failed to list tools: ${messageOf(e)}`);
209
+ return endpointFailure(e, `Failed to list tools: ${messageOf(e)}`);
208
210
  }
209
211
  if (!names.has(toolName)) {
210
212
  this.#cache.delete(endpoint);
@@ -222,7 +224,7 @@ var McpToolResolver = class {
222
224
  });
223
225
  return this.#normalizeCallResult(raw);
224
226
  } catch (e) {
225
- return failure$1("MCP_ENDPOINT_UNAVAILABLE", `Tool "${toolName}" on endpoint "${endpoint}" failed: ${messageOf(e)}`);
227
+ return endpointFailure(e, `Tool "${toolName}" on endpoint "${endpoint}" failed: ${messageOf(e)}`);
226
228
  }
227
229
  }
228
230
  async #toolNames(endpoint, client) {
@@ -392,15 +394,39 @@ const failure = (code, message) => ({
392
394
  message
393
395
  }
394
396
  });
395
- /** listTools 返回值提取工具描述(容忍数组或 {tools:[]} 两种形状) */
397
+ /** getTools() 返回的工具 描述符:inputSchema JSON 字符串时解析为对象,并保留原始对象 */
398
+ function toDescriptor(raw) {
399
+ if (typeof raw !== "object" || raw === null) return void 0;
400
+ const t = raw;
401
+ const name = String(t["name"] ?? "");
402
+ if (name === "") return void 0;
403
+ const descriptor = {
404
+ name,
405
+ raw
406
+ };
407
+ if (typeof t["description"] === "string") descriptor.description = t["description"];
408
+ if (typeof t["origin"] === "string") descriptor.origin = t["origin"];
409
+ if (typeof t["annotations"] === "object" && t["annotations"] !== null) {
410
+ const annotations = t["annotations"];
411
+ descriptor.annotations = {
412
+ ...typeof annotations["readOnlyHint"] === "boolean" ? { readOnlyHint: annotations["readOnlyHint"] } : {},
413
+ ...typeof annotations["untrustedContentHint"] === "boolean" ? { untrustedContentHint: annotations["untrustedContentHint"] } : {}
414
+ };
415
+ }
416
+ const schema = t["inputSchema"];
417
+ if (typeof schema === "string" && schema.trim() !== "") try {
418
+ descriptor.inputSchema = JSON.parse(schema);
419
+ } catch {
420
+ descriptor.inputSchema = schema;
421
+ }
422
+ else if (typeof schema === "object" && schema !== null) descriptor.inputSchema = schema;
423
+ return descriptor;
424
+ }
425
+ /** 从 getTools 返回值提取工具描述(容忍数组或 {tools:[]} 两种形状) */
396
426
  function extractToolDescriptors(raw) {
397
427
  const list = Array.isArray(raw) ? raw : typeof raw === "object" && raw !== null && Array.isArray(raw.tools) ? raw.tools : void 0;
398
428
  if (!list) return void 0;
399
- return list.filter((t) => typeof t === "object" && t !== null).map((t) => ({
400
- name: String(t["name"] ?? ""),
401
- ...typeof t["description"] === "string" ? { description: t["description"] } : {},
402
- ...typeof t["inputSchema"] === "object" && t["inputSchema"] !== null ? { inputSchema: t["inputSchema"] } : {}
403
- })).filter((t) => t.name !== "");
429
+ return list.map(toDescriptor).filter((d) => d !== void 0);
404
430
  }
405
431
  /**
406
432
  * Experimental WebMCP Adapter(mcp# 分支):默认关闭显式启用;
@@ -416,35 +442,45 @@ var ExperimentalWebMcpAdapter = class {
416
442
  this.#enabled = options?.enabled ?? false;
417
443
  }
418
444
  isAvailable() {
419
- return this.#enabled && typeof this.#resolveApi()?.executeTool === "function";
445
+ const api = this.#resolveApi();
446
+ return this.#enabled && typeof api?.executeTool === "function";
420
447
  }
421
- /** 工具清单(listTools 缺失时返回 undefined;调用失败时告警后返回 undefined) */
448
+ /** 工具清单(getTools 缺失时返回 undefined;调用失败时告警后返回 undefined) */
422
449
  async listTools() {
423
450
  const api = this.#resolveApi();
424
- if (typeof api?.listTools !== "function") return void 0;
451
+ if (typeof api?.getTools !== "function") return void 0;
425
452
  try {
426
- return extractToolDescriptors(await api.listTools());
453
+ return extractToolDescriptors(await api.getTools());
427
454
  } catch (e) {
428
- console.warn(`[webskill] navigator.modelContext.listTools() failed: ${e instanceof Error ? e.message : String(e)}`);
455
+ console.warn(`[webskill] document.modelContext.getTools() failed: ${e instanceof Error ? e.message : String(e)}`);
429
456
  return;
430
457
  }
431
458
  }
432
- /** 工具名清单(listTools 缺失/失败时返回 undefined) */
459
+ /** 工具名清单(getTools 缺失/失败时返回 undefined) */
433
460
  async listToolNames() {
434
461
  return (await this.listTools())?.map((t) => t.name);
435
462
  }
436
463
  async call(toolName, args) {
437
464
  if (!this.#enabled) return failure("TOOL_UNSUPPORTED", "WebMCP adapter is disabled (enable it explicitly to use mcp# tools)");
438
465
  const api = this.#resolveApi();
439
- if (typeof api?.executeTool !== "function") return failure("TOOL_UNSUPPORTED", "navigator.modelContext is not available");
440
- if (typeof api.listTools === "function") try {
441
- const names = extractToolDescriptors(await api.listTools())?.map((t) => t.name);
442
- if (names && !names.includes(toolName)) return failure("MCP_TOOL_NOT_FOUND", `WebMCP tool "${toolName}" not found`);
443
- } catch {}
466
+ if (typeof api?.executeTool !== "function") return failure("TOOL_UNSUPPORTED", "document.modelContext is not available");
467
+ const argsJson = JSON.stringify(args);
468
+ if (typeof api.getTools === "function") try {
469
+ const descriptors = extractToolDescriptors(await api.getTools());
470
+ const match = descriptors?.find((t) => t.name === toolName);
471
+ if (descriptors !== void 0 && match === void 0) return failure("MCP_TOOL_NOT_FOUND", `WebMCP tool "${toolName}" not found`);
472
+ const tool = match?.raw ?? { name: toolName };
473
+ return {
474
+ ok: true,
475
+ content: normalizeToolContent(await api.executeTool(tool, argsJson))
476
+ };
477
+ } catch (e) {
478
+ return failure("TOOL_EXECUTION_FAILED", `WebMCP tool "${toolName}" failed: ${messageOf(e)}`);
479
+ }
444
480
  try {
445
481
  return {
446
482
  ok: true,
447
- content: normalizeToolContent(await api.executeTool(toolName, JSON.stringify(args)))
483
+ content: normalizeToolContent(await api.executeTool(toolName, argsJson))
448
484
  };
449
485
  } catch (e) {
450
486
  return failure("TOOL_EXECUTION_FAILED", `WebMCP tool "${toolName}" failed: ${messageOf(e)}`);
@@ -531,22 +567,182 @@ var McpRuntimePlugin = class {
531
567
  };
532
568
  }
533
569
  };
570
+ /**
571
+ * 构造授权相关错误。`message` 只含端点名与阶段,`details` 只有 `{ endpoint, stage }`——
572
+ * 不带 `cause`:SDK 的错误 message 可能回显授权 URL 或 token 端点的响应体(AC-22.6)。
573
+ */
574
+ function oauthError(code, endpoint, stage, reason) {
575
+ return new WebSkillError(code, `Remote MCP endpoint "${endpoint}" ${reason} (stage: ${stage})`, {
576
+ endpoint,
577
+ stage
578
+ });
579
+ }
580
+ /** 显式的内存存储,供 examples 与测试使用。**不是缺省行为**——是宿主主动选的(AC-22.7)。 @experimental */
581
+ function createMemoryOAuthStores() {
582
+ const tokens = /* @__PURE__ */ new Map();
583
+ const handshakes = /* @__PURE__ */ new Map();
584
+ return {
585
+ tokens: {
586
+ load: (endpoint) => Promise.resolve(tokens.get(endpoint)),
587
+ save: (endpoint, value) => {
588
+ tokens.set(endpoint, value);
589
+ return Promise.resolve();
590
+ },
591
+ clear: (endpoint) => {
592
+ tokens.delete(endpoint);
593
+ return Promise.resolve();
594
+ }
595
+ },
596
+ handshake: {
597
+ load: (endpoint) => Promise.resolve(handshakes.get(endpoint)),
598
+ save: (endpoint, value) => {
599
+ handshakes.set(endpoint, value);
600
+ return Promise.resolve();
601
+ },
602
+ clear: (endpoint) => {
603
+ handshakes.delete(endpoint);
604
+ return Promise.resolve();
605
+ }
606
+ }
607
+ };
608
+ }
609
+ const randomState = () => {
610
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
611
+ crypto.getRandomValues(bytes);
612
+ return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
613
+ };
614
+ /**
615
+ * `McpOAuthConfig` → SDK `OAuthClientProvider` 适配器。
616
+ *
617
+ * `state()` 与 `saveCodeVerifier()` 由 SDK 分两次调用(顺序上前者在先),
618
+ * 两者合成一条握手记录:谁先到都把当前已知部分写进去,回程时两项都在。
619
+ * @experimental
620
+ */
621
+ function createOAuthProvider(endpoint, config) {
622
+ const { client, tokens: tokenStore, handshake } = config;
623
+ let pending = {};
624
+ const persist = async (patch) => {
625
+ pending = {
626
+ ...pending,
627
+ ...patch
628
+ };
629
+ await handshake.save(endpoint, {
630
+ codeVerifier: pending.codeVerifier ?? "",
631
+ state: pending.state ?? ""
632
+ });
633
+ };
634
+ return {
635
+ get redirectUrl() {
636
+ return client.redirectUri;
637
+ },
638
+ get clientMetadata() {
639
+ return {
640
+ client_name: "WebSkill SDK",
641
+ redirect_uris: [client.redirectUri],
642
+ grant_types: ["authorization_code", "refresh_token"],
643
+ response_types: ["code"],
644
+ token_endpoint_auth_method: client.clientSecret === void 0 ? "none" : "client_secret_post",
645
+ ...client.scopes && client.scopes.length > 0 ? { scope: client.scopes.join(" ") } : {}
646
+ };
647
+ },
648
+ async state() {
649
+ const value = randomState();
650
+ await persist({ state: value });
651
+ return value;
652
+ },
653
+ clientInformation() {
654
+ return Promise.resolve({
655
+ client_id: client.clientId,
656
+ ...client.clientSecret !== void 0 ? { client_secret: client.clientSecret } : {}
657
+ });
658
+ },
659
+ async tokens() {
660
+ const saved = await tokenStore.load(endpoint);
661
+ if (!saved) return void 0;
662
+ return {
663
+ access_token: saved.accessToken,
664
+ token_type: saved.tokenType ?? "Bearer",
665
+ ...saved.expiresAt !== void 0 ? { expires_in: Math.max(0, Math.round((saved.expiresAt - Date.now()) / 1e3)) } : {},
666
+ ...saved.refreshToken !== void 0 ? { refresh_token: saved.refreshToken } : {},
667
+ ...saved.scope !== void 0 ? { scope: saved.scope } : {}
668
+ };
669
+ },
670
+ async saveTokens(next) {
671
+ await tokenStore.save(endpoint, {
672
+ accessToken: next.access_token,
673
+ ...next.expires_in !== void 0 ? { expiresAt: Date.now() + next.expires_in * 1e3 } : {},
674
+ ...next.refresh_token !== void 0 ? { refreshToken: next.refresh_token } : {},
675
+ ...next.token_type !== void 0 ? { tokenType: next.token_type } : {},
676
+ ...next.scope !== void 0 ? { scope: next.scope } : {}
677
+ });
678
+ },
679
+ async redirectToAuthorization(url) {
680
+ await config.openAuthorization(url);
681
+ },
682
+ async saveCodeVerifier(codeVerifier) {
683
+ await persist({ codeVerifier });
684
+ },
685
+ async codeVerifier() {
686
+ const saved = await handshake.load(endpoint);
687
+ if (!saved?.codeVerifier) throw oauthError("MCP_OAUTH_FAILED", endpoint, "exchange", "has no stored PKCE verifier for this handshake");
688
+ return saved.codeVerifier;
689
+ },
690
+ async invalidateCredentials(scope) {
691
+ if (scope === "all" || scope === "tokens") await tokenStore.clear(endpoint);
692
+ if (scope === "all" || scope === "verifier") await handshake.clear(endpoint);
693
+ }
694
+ };
695
+ }
534
696
  async function loadMcpSdk() {
535
697
  try {
536
- const [client, sse, streamable] = await Promise.all([
698
+ const [client, sse, streamable, auth, errors] = await Promise.all([
537
699
  import("@modelcontextprotocol/sdk/client/index.js"),
538
700
  import("@modelcontextprotocol/sdk/client/sse.js"),
539
- import("@modelcontextprotocol/sdk/client/streamableHttp.js")
701
+ import("@modelcontextprotocol/sdk/client/streamableHttp.js"),
702
+ import("@modelcontextprotocol/sdk/client/auth.js"),
703
+ import("@modelcontextprotocol/sdk/server/auth/errors.js")
540
704
  ]);
541
705
  return {
542
706
  Client: client.Client,
543
707
  SSEClientTransport: sse.SSEClientTransport,
544
- StreamableHTTPClientTransport: streamable.StreamableHTTPClientTransport
708
+ StreamableHTTPClientTransport: streamable.StreamableHTTPClientTransport,
709
+ UnauthorizedError: auth.UnauthorizedError,
710
+ OAuthError: errors.OAuthError
545
711
  };
546
712
  } catch (e) {
547
713
  throw new WebSkillError("MCP_ENDPOINT_UNAVAILABLE", "The \"@modelcontextprotocol/sdk\" package is required for remote MCP endpoints; install it first (npm i @modelcontextprotocol/sdk)", e);
548
714
  }
549
715
  }
716
+ /** 运行期的未授权(含刷新失败)必须在 endpoint 层转成 `WebSkillError`,否则 401 会以工具结果的形式进模型上下文 */
717
+ function wrapOAuthClient(client, endpoint, oauth, isUnauthorized) {
718
+ const guard = async (work) => {
719
+ try {
720
+ return await work();
721
+ } catch (e) {
722
+ if (!isUnauthorized(e)) throw e;
723
+ await oauth.tokens.clear(endpoint);
724
+ throw oauthError("MCP_OAUTH_REQUIRED", endpoint, "refresh", "needs authorization again");
725
+ }
726
+ };
727
+ return {
728
+ listTools: () => guard(() => client.listTools()),
729
+ callTool: (input) => guard(() => client.callTool(input)),
730
+ listPrompts: () => guard(() => client.listPrompts()),
731
+ getPrompt: (input) => guard(() => client.getPrompt(input)),
732
+ listResources: () => guard(() => client.listResources()),
733
+ readResource: (input) => guard(() => client.readResource(input))
734
+ };
735
+ }
736
+ /** 授权注入的完整性校验:缺一项就是宿主的配置错误,不是运行期授权问题(AC-22.7) */
737
+ function assertOAuthConfigured(endpoint, oauth) {
738
+ const missing = [];
739
+ if (!oauth.client?.clientId) missing.push("client.clientId");
740
+ if (!oauth.client?.redirectUri) missing.push("client.redirectUri");
741
+ if (!oauth.tokens) missing.push("tokens");
742
+ if (!oauth.handshake) missing.push("handshake");
743
+ if (typeof oauth.openAuthorization !== "function") missing.push("openAuthorization");
744
+ if (missing.length > 0) throw oauthError("MCP_OAUTH_NOT_CONFIGURED", endpoint, "discover", `is missing OAuth wiring: ${missing.join(", ")}`);
745
+ }
550
746
  /**
551
747
  * 远程 MCP endpoint 装配:SDK 官方 StreamableHTTPClientTransport(默认)/
552
748
  * SSEClientTransport(遗留)连接远端 server,注册进 EndpointRegistry——
@@ -554,7 +750,7 @@ async function loadMcpSdk() {
554
750
  * 返回 close 句柄:断开后 unregister(临时技能随既有生命周期自然消失)。
555
751
  */
556
752
  async function connectRemoteEndpoint(registry, config) {
557
- const { Client, SSEClientTransport, StreamableHTTPClientTransport } = await loadMcpSdk();
753
+ const { Client, SSEClientTransport, StreamableHTTPClientTransport, UnauthorizedError, OAuthError } = await loadMcpSdk();
558
754
  let url;
559
755
  try {
560
756
  url = new URL(config.url);
@@ -565,46 +761,81 @@ async function connectRemoteEndpoint(registry, config) {
565
761
  allowHttp: config.allowHttp ?? false,
566
762
  allowPrivateHosts: config.allowPrivateHosts ?? false
567
763
  });
764
+ const oauth = config.oauth;
765
+ if (oauth) assertOAuthConfigured(config.endpoint, oauth);
766
+ const authProvider = oauth ? createOAuthProvider(config.endpoint, oauth) : void 0;
568
767
  const requestInit = config.headers ? { headers: config.headers } : void 0;
569
- const transport = config.transport === "sse" ? new SSEClientTransport(url, { ...requestInit ? { requestInit } : {} }) : new StreamableHTTPClientTransport(url, { ...requestInit ? { requestInit } : {} });
768
+ const transportOptions = {
769
+ ...requestInit ? { requestInit } : {},
770
+ ...authProvider ? { authProvider } : {}
771
+ };
772
+ const makeTransport = () => config.transport === "sse" ? new SSEClientTransport(url, transportOptions) : new StreamableHTTPClientTransport(url, transportOptions);
570
773
  const client = new Client({
571
774
  name: "webskill-remote-client",
572
775
  version: "0.1.0"
573
776
  });
777
+ const isUnauthorized = (e) => e instanceof UnauthorizedError || e instanceof OAuthError;
574
778
  const unavailable = (e) => new WebSkillError("MCP_ENDPOINT_UNAVAILABLE", `Failed to connect remote MCP endpoint "${config.endpoint}" at ${config.url}: ${messageOf(e)}`, e);
575
- try {
779
+ let closed = false;
780
+ const close = async () => {
781
+ if (closed) return;
782
+ closed = true;
783
+ registry.unregister(config.endpoint);
784
+ try {
785
+ await client.close();
786
+ } catch (e) {
787
+ throw new WebSkillError("MCP_ENDPOINT_UNAVAILABLE", `Failed to close remote MCP endpoint "${config.endpoint}": ${messageOf(e)}`, e);
788
+ }
789
+ };
790
+ const register = () => {
791
+ try {
792
+ const entry = client;
793
+ registry.register(config.endpoint, oauth ? wrapOAuthClient(entry, config.endpoint, oauth, isUnauthorized) : entry);
794
+ } catch (e) {
795
+ client.close().catch(() => void 0);
796
+ throw unavailable(e);
797
+ }
798
+ };
799
+ const openConnection = async (transport) => {
576
800
  let connect = client.connect(transport);
577
801
  if (config.timeoutMs && config.timeoutMs > 0) {
578
802
  const timeoutMs = config.timeoutMs;
579
803
  connect = Promise.race([connect, new Promise((_resolve, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Connection timed out after ${timeoutMs}ms`)), timeoutMs))]);
580
804
  }
581
805
  await connect;
582
- } catch (e) {
806
+ };
807
+ const finishAuthorization = async (code, state) => {
808
+ const saved = await oauth.handshake.load(config.endpoint);
809
+ if (!saved || saved.state === "" || saved.state !== state) throw oauthError("MCP_OAUTH_FAILED", config.endpoint, "authorize", "received a mismatched authorization state");
810
+ const transport = makeTransport();
583
811
  try {
584
- await client.close();
585
- } catch {}
586
- throw unavailable(e);
587
- }
812
+ await transport.finishAuth(code);
813
+ } catch (e) {
814
+ throw oauthError(isUnauthorized(e) ? "MCP_OAUTH_REQUIRED" : "MCP_OAUTH_FAILED", config.endpoint, "exchange", "could not exchange the authorization code");
815
+ }
816
+ await oauth.handshake.clear(config.endpoint);
817
+ await openConnection(transport);
818
+ register();
819
+ };
588
820
  try {
589
- registry.register(config.endpoint, client);
821
+ await openConnection(makeTransport());
590
822
  } catch (e) {
823
+ if (oauth && isUnauthorized(e)) return {
824
+ close,
825
+ finishAuthorization,
826
+ authorizationRequired: true
827
+ };
591
828
  try {
592
829
  await client.close();
593
830
  } catch {}
594
831
  throw unavailable(e);
595
832
  }
596
- let closed = false;
597
- return { close: async () => {
598
- if (closed) return;
599
- closed = true;
600
- registry.unregister(config.endpoint);
601
- try {
602
- await client.close();
603
- } catch (e) {
604
- throw new WebSkillError("MCP_ENDPOINT_UNAVAILABLE", `Failed to close remote MCP endpoint "${config.endpoint}": ${messageOf(e)}`, e);
605
- }
606
- } };
833
+ register();
834
+ return {
835
+ close,
836
+ ...oauth ? { finishAuthorization } : {}
837
+ };
607
838
  }
608
839
 
609
840
  //#endregion
610
- export { EndpointRegistry, ExperimentalWebMcpAdapter, McpRuntimePlugin, McpToolResolver, MessageChannelTransport, TemporarySkillProvider, catalogMerge, connectRemoteEndpoint, endpointToolLlmName, parseEndpointToolLlmName, parseWebMcpToolLlmName, serveSkillAsMcp, validateJsonRpcMessage, webMcpToolLlmName };
841
+ export { EndpointRegistry, ExperimentalWebMcpAdapter, McpRuntimePlugin, McpToolResolver, MessageChannelTransport, TemporarySkillProvider, catalogMerge, connectRemoteEndpoint, createMemoryOAuthStores, createOAuthProvider, endpointToolLlmName, parseEndpointToolLlmName, parseWebMcpToolLlmName, serveSkillAsMcp, validateJsonRpcMessage, webMcpToolLlmName };
@@ -1,6 +1,6 @@
1
1
  import { m as WebSkillError } from "./dist-8oQRa8Xz.js";
2
2
 
3
- //#region ../runtime/dist/memoryArtifactStore-BtOeB_hm.js
3
+ //#region ../runtime/dist/memoryArtifactStore-52Zn9npI.js
4
4
  /** 纯文本消息内容的构造快捷方式(引擎内部绝大多数消息仍是纯文本) */
5
5
  const textParts = (text) => [{
6
6
  type: "text",
@@ -8,6 +8,14 @@ const textParts = (text) => [{
8
8
  }];
9
9
  /** 取出 parts 中的文本(非文本分片在纯文本语境下无法表达,此处按丢弃处理——调用方须先校验) */
10
10
  const partsToText = (parts) => (parts ?? []).reduce((acc, part) => part.type === "text" ? acc + part.text : acc, "");
11
+ /**
12
+ * run 的提示词文本投影:`RuntimeRun.userPrompt` 与快照都是字符串字段,
13
+ * 而多模态提示词里的图片/文件分片没有文本形态,用占位符标注,避免记录成空串。
14
+ */
15
+ const promptText = (prompt) => {
16
+ if (typeof prompt === "string") return prompt;
17
+ return prompt.map((part) => part.type === "text" ? part.text : `[${part.type}: ${part.mimeType}]`).join("\n");
18
+ };
11
19
  const isContentPart = (value) => {
12
20
  if (typeof value !== "object" || value === null) return false;
13
21
  const part = value;
@@ -75,4 +83,4 @@ var MemoryArtifactStore = class {
75
83
  };
76
84
 
77
85
  //#endregion
78
- export { validateLlmMessages as a, textParts as i, partsToText as n, rejectUnsupportedPart as r, MemoryArtifactStore as t };
86
+ export { textParts as a, rejectUnsupportedPart as i, partsToText as n, validateLlmMessages as o, promptText as r, MemoryArtifactStore as t };
package/dist/node.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { I as JsonSchema, N as FileStat, O as ArchiveLimits, P as FileSystemProvider, U as SKILLS_LOCKFILE, W as SKILL_MANIFEST_FILE, Y as SignatureAuditSink, _t as VerifyResult, at as SkillManifest, b as UiBridge, c as InteractionResponse, dt as SkillsLockfile, ht as UnsignedPolicy, it as SkillManagerPort, pt as TrustedKeyStore, s as InteractionRequest, tt as SkillInstallSource, y as RenderResultRequest } from "./types-D_hoCri8-BnNPiZCi.js";
2
- import { D as FsMemoryStore, E as FsArtifactStore, Ht as WebSkillRuntimeDeps, Nt as ToolResult, Vt as WebSkillRuntime, W as NetworkPolicy, _t as SchemaInferer, d as ApprovalScope, f as BridgeCapabilities, jt as ToolDefinition, qt as createScriptContext, vt as ScriptExecutionContext, yt as ScriptExecutor } from "./index-vBz_FC9w.js";
3
- import { a as AuditLog, p as CandidateStore, r as ApprovalPolicy, u as CandidateSkill, v as SkillVersionStore } from "./skillVersionStore-BzLbzFOL-CxwIewHJ.js";
4
- import { n as LlmEnvConfig, s as probeLlmCapabilities, t as LlmCapabilities } from "./env-BPUBZCwJ-4jat_SVG.js";
1
+ import { A as ArchiveLimits, F as FileStat, G as SKILLS_LOCKFILE, I as FileSystemProvider, K as SKILL_MANIFEST_FILE, R as JsonSchema, Z as SignatureAuditSink, _t as UnsignedPolicy, c as InteractionResponse, ht as TrustedKeyStore, ot as SkillManagerPort, pt as SkillsLockfile, rt as SkillInstallSource, s as InteractionRequest, st as SkillManifest, x as UiBridge, y as RenderResultRequest, yt as VerifyResult } from "./types-CcxRLdJG-DCXyw1US.js";
2
+ import { Et as ScriptExecutor, Kt as ToolResult, M as FsMemoryStore, Tt as ScriptExecutionContext, Wt as ToolDefinition, Y as NetworkPolicy, _ as BridgeCapabilities, f as ApprovalScope, hn as createScriptContext, j as FsArtifactStore, ln as WebSkillRuntime, un as WebSkillRuntimeDeps, wt as SchemaInferer } from "./index-DkbABR43.js";
3
+ import { a as AuditLog, p as CandidateStore, r as ApprovalPolicy, u as CandidateSkill, v as SkillVersionStore } from "./skillVersionStore-Bl-ElD45-CWPvGvoq.js";
4
+ import { n as LlmEnvConfig, s as probeLlmCapabilities, t as LlmCapabilities } from "./env-AK3cSMEA-Dli6QU5E.js";
5
5
  import { Readable, Writable } from "node:stream";
6
6
  //#region ../node/dist/index.d.ts
7
7
  //#region src/fs/nodeFs.d.ts
package/dist/node.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { A as parseSkillMarkdown, B as unzipWithLimits, H as verifyManifest, I as resolveArchiveLimits, L as resolveInsideRoot, M as readResponseWithLimit, N as readSkillSignature, O as messageOf, T as isValidSkillName, U as verifySkillSignature, V as validateSkills, _ as atomicWriteText, g as assertSafePathSegment, h as assertRemoteUrlAllowed, j as parseSkillPackManifest, m as WebSkillError, o as SKILLS_LOCKFILE, r as MANIFEST_EXCLUDED_FILES, s as SKILL_MANIFEST_FILE, u as SKILL_PACK_FILE, w as exportSkills, y as buildManifest } from "./dist-8oQRa8Xz.js";
2
- import { J as normalizeToolError, N as createScriptContext, O as WebSkillRuntime, W as networkPolicyLibSource, Y as parseBridgeRequest, d as FsMemoryStore, k as bridgeError, o as CapabilityApproval, q as normalizeToolContent, u as FsArtifactStore } from "./dist-6C03DShK.js";
3
- import { i as probeLlmCapabilities } from "./env--jJB-TSX-04klhTYi.js";
2
+ import { B as bridgeError, H as createScriptContext, L as WebSkillRuntime, c as CapabilityApproval, ct as normalizeToolError, h as FsMemoryStore, it as networkPolicyLibSource, lt as parseBridgeRequest, m as FsArtifactStore, st as normalizeToolContent } from "./dist-D0qW6e40.js";
3
+ import { i as probeLlmCapabilities } from "./env-8cY40DXB-CGnEVZby.js";
4
+ import { t as AUDIT_EVENT_TYPES } from "./eventTypes-DjIQpt8Y-Bj3vghj4.js";
4
5
  import { createRequire } from "node:module";
5
6
  import { unzipSync, zipSync } from "fflate";
6
7
  import { existsSync, promises, realpathSync } from "node:fs";
@@ -1148,6 +1149,13 @@ var CliUiBridge = class {
1148
1149
  value: answer === "y" || answer === "yes"
1149
1150
  };
1150
1151
  }
1152
+ case "file-pick": {
1153
+ const answer = (await this.#question(`${input.message}\nLocal file path (empty to decline): `)).trim();
1154
+ return answer === "" ? { id: input.id } : {
1155
+ id: input.id,
1156
+ value: { path: answer }
1157
+ };
1158
+ }
1151
1159
  }
1152
1160
  }
1153
1161
  async progress(input) {
@@ -2252,7 +2260,7 @@ var ApprovalWorkflow = class {
2252
2260
  });
2253
2261
  await this.#store.updateStatus(candidateId, "published");
2254
2262
  await this.#audit.append({
2255
- type: "skill.published",
2263
+ type: AUDIT_EVENT_TYPES.skillPublished,
2256
2264
  target: candidate.name,
2257
2265
  actor: input.actor,
2258
2266
  data: {
@@ -1,5 +1,5 @@
1
- import { nt as uiCatalog } from "./dist-bewtXYlO.js";
2
- import { t as CatalogNode } from "./catalogComponents-DV7cPpUm-C77AEEx9.js";
1
+ import { nt as uiCatalog } from "./dist-DnYG2-eY.js";
2
+ import { t as CatalogNode } from "./catalogComponents-Dr5dFMAb-DKH_7VPI.js";
3
3
  import { z } from "zod";
4
4
  import { Component, Fragment, createContext, useCallback, useContext, useEffect, useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -3761,7 +3761,7 @@ function Renderer({ response, library, isStreaming = false, onAction, onStateUpd
3761
3761
  const FormValidationContext = createContext(null);
3762
3762
 
3763
3763
  //#endregion
3764
- //#region ../ui-react/dist/openUiLibrary-W3Ce896k.js
3764
+ //#region ../ui-react/dist/openUiLibrary-Bdrji9qK.js
3765
3765
  const propsFor = (props, container) => container ? props.extend({ children: z.array(z.any()).optional() }) : props;
3766
3766
  /**
3767
3767
  * catalog 的 OpenUI 投影:`defineComponent` 复用同一份 zod schema 与描述,
@@ -1,5 +1,5 @@
1
- import { B as PageQuery, P as FileSystemProvider, Q as SkillCatalogEntry, at as SkillManifest, z as Page } from "./types-D_hoCri8-BnNPiZCi.js";
2
- //#region ../governance/dist/skillVersionStore-BzLbzFOL.d.ts
1
+ import { H as PageQuery, I as FileSystemProvider, V as Page, et as SkillCatalogEntry, st as SkillManifest } from "./types-CcxRLdJG-DCXyw1US.js";
2
+ //#region ../governance/dist/skillVersionStore-Bl-ElD45.d.ts
3
3
  //#region src/types.d.ts
4
4
  type CandidateStatus = 'draft' | 'pending-review' | 'approved' | 'published' | 'rejected';
5
5
  /** `generated` 是 0.5.0 的技能自动生成来源(需求 12 号 AC-9.1) */
@@ -49,6 +49,12 @@ interface AuditQueryFilter {
49
49
  since?: string;
50
50
  /** 含端点(`ts <= until`) */
51
51
  until?: string;
52
+ /**
53
+ * true 时链校验失败不抛错,改为在结果里报告断点(`chainBrokenAt` / `chainReason`)。
54
+ * **仅用于人工查看的降级展示**:程序化消费方(合规导出等)拿到部分结果而不知情
55
+ * 比直接失败更危险。默认 false,保持既有行为。
56
+ */
57
+ tolerateBrokenChain?: boolean;
52
58
  }
53
59
  interface AuditLog {
54
60
  append(event: Omit<AuditEvent, 'id' | 'ts'> & {
@@ -1,5 +1,5 @@
1
1
  import { m as WebSkillError } from "./dist-8oQRa8Xz.js";
2
- import { i as textParts, n as partsToText } from "./memoryArtifactStore-BtOeB_hm-tj3fC5ip.js";
2
+ import { a as textParts, n as partsToText } from "./memoryArtifactStore-52Zn9npI-BMPYwvoy.js";
3
3
 
4
4
  //#region ../runtime/dist/testing.js
5
5
  /**
package/dist/testing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { _ as MemoryStore, b as UiBridge, c as InteractionResponse, l as LlmClient, m as LlmStreamEvent, n as ArtifactStore, p as LlmResponse, s as InteractionRequest, t as Artifact, u as LlmCompleteInput } from "./types-D_hoCri8-BnNPiZCi.js";
2
- import { a as loadGoogleConfigFromEnv, i as loadAnthropicConfigFromEnv, n as LlmEnvConfig, o as loadLlmConfigFromEnv, r as ProviderEnvConfig } from "./env-BPUBZCwJ-4jat_SVG.js";
1
+ import { _ as MemoryStore, c as InteractionResponse, l as LlmClient, m as LlmStreamEvent, n as ArtifactStore, p as LlmResponse, s as InteractionRequest, t as Artifact, u as LlmCompleteInput, x as UiBridge } from "./types-CcxRLdJG-DCXyw1US.js";
2
+ import { a as loadGoogleConfigFromEnv, i as loadAnthropicConfigFromEnv, n as LlmEnvConfig, o as loadLlmConfigFromEnv, r as ProviderEnvConfig } from "./env-AK3cSMEA-Dli6QU5E.js";
3
3
  //#region ../runtime/dist/testing.d.ts
4
4
  //#region src/llm/mockLlmClient.d.ts
5
5
  type MockLlmHandler = (input: LlmCompleteInput) => LlmResponse | Promise<LlmResponse>;
package/dist/testing.js CHANGED
@@ -1,5 +1,5 @@
1
- import { t as MemoryArtifactStore } from "./memoryArtifactStore-BtOeB_hm-tj3fC5ip.js";
2
- import { n as loadGoogleConfigFromEnv, r as loadLlmConfigFromEnv, t as loadAnthropicConfigFromEnv } from "./env--jJB-TSX-04klhTYi.js";
3
- import { n as MockLlmClient, r as MockUiBridge, t as InMemoryStore } from "./testing-DDCJWvgA.js";
1
+ import { t as MemoryArtifactStore } from "./memoryArtifactStore-52Zn9npI-BMPYwvoy.js";
2
+ import { n as loadGoogleConfigFromEnv, r as loadLlmConfigFromEnv, t as loadAnthropicConfigFromEnv } from "./env-8cY40DXB-CGnEVZby.js";
3
+ import { n as MockLlmClient, r as MockUiBridge, t as InMemoryStore } from "./testing-CYTFqkDm.js";
4
4
 
5
5
  export { InMemoryStore, MemoryArtifactStore, MockLlmClient, MockUiBridge, loadAnthropicConfigFromEnv, loadGoogleConfigFromEnv, loadLlmConfigFromEnv };