@webskill/sdk 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -6,6 +6,6 @@ import { $ as IntegrityVerdict, $n as diffUserProfile, $t as SessionMeta, A as D
6
6
  * Version of the published `@webskill/sdk` package, injected at build time.
7
7
  * @stable
8
8
  */
9
- declare const SDK_VERSION = "0.17.0";
9
+ declare const SDK_VERSION = "0.19.0";
10
10
  //#endregion
11
11
  export { ALLOWED_TOOLS_EXCLUSION_REASON, ASK_USER_FIELD_TYPES, ASK_USER_INPUT_SCHEMA, ASK_USER_MAX_FIELDS, ASK_USER_TOOL, ASK_USER_TOOL_NAME, ATOMIC_TMP_SUFFIX_PATTERN, ATTACHMENT_TEXT_LIMIT, type ActivateLifecycleData, AgentLoop, type AgentLoopConfig, type AgentLoopDeps, AnthropicClient, type AnthropicClientConfig, type ApprovalDecision, type ApprovalScope, type ArchiveLimits, type Artifact, type ArtifactStore, type AttachmentTextInput, BEHAVIOR_RECORDS_KEY, type BehaviorRecord, type BehaviorRecordKind, type BehaviorScene, type BridgeCapabilities, type BridgeCapability, type BridgeRequest, type BridgeResponse, CapabilityApproval, type CapabilityMode, type CatalogRenderer, type ChartSpec, type ChatAttachmentKind, type CryptoKeyLike, DEFAULT_ARCHIVE_LIMITS, DEFAULT_LOOP_LIMITS, DEFAULT_MAX_DATA_SOURCE_BYTES, DEFAULT_MAX_DOCUMENT_BYTES, DEFAULT_MAX_DOCUMENT_TEXT_BYTES, DEFAULT_MAX_UPLOAD_FILE_BYTES, DEFAULT_USER_PROFILE_LIMITS, DOCX_MIME, type DataSourceInfo, type DiscoveryResult, type DocumentSurfaceHost, type DocumentSurfacePort, type DocxTextExtractor, EMPTY_USER_PROFILE, EventBus, type ExecuteLifecycleData, type ExternalSkillProvider, type ExternalToolSource, FILE_MIME_TYPES, FS_SESSION_PAGE_SIZE, type FileStat, type FileSystemProvider, type FormField, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FsToolStepStore, FsTrustedKeyStore, FullDisclosureRouter, GoogleGenAiClient, type GoogleGenAiClientConfig, HookRunner, type HookRunnerOptions, IMAGE_MIME_TYPES, type InstalledSkillManifest, type IntegrityVerdict, type InteractLifecycleData, type InteractionOrigin, type InteractionPolicy, type InteractionRequest, type InteractionResponse, type JsonSchema, type LifecycleEvent, type LifecycleEventInit, type LifecycleHook, type LifecycleHookContext, type LifecycleListener, type LinkedDocumentReader, type LlmClient, type LlmCompleteInput, type LlmContentPart, type LlmMessage, type LlmResponse, type LlmStreamEvent, type LlmTokenUsage, type LlmToolCall, type LlmToolSpec, MANIFEST_EXCLUDED_FILES, MAX_TOOL_STEP_ARG_BYTES, MemoryFS, type MemoryStore, type NetworkPolicy, OpenAiCompatibleClient, type OpenAiCompatibleClientConfig, type Page, type PageQuery, type PdfTextExtractor, ProgressiveRouter, READ_LINKED_DOCUMENT_TOOL, READ_LINKED_DOCUMENT_TOOL_NAME, READ_SKILL_FILE_INPUT_SCHEMA, READ_SKILL_FILE_TOOL, READ_SKILL_FILE_TOOL_NAME, RUN_SNAPSHOT_SCHEMA_VERSION, RUN_TRACE_SCHEMA_VERSION, type RedactedArgs, type RefineUserProfileInput, type RemoteUrlPolicy, type RenderBlock, type RenderResultRequest, type RouteLifecycleData, type RouteResult, type RunLimitErrorDetails, type RunResult, type RunSnapshot, type RunSnapshotListEntry, type RunSnapshotStore, type RunTerminationReason, type RunToolCall, type RunTraceFile, type RunTraceFilter, type RunTraceMetrics, type RunTraceStore, type RunTraceSummary, type RunUploadFiles, type RunUsageSummary, type RuntimePhase, type RuntimeRun, type RuntimeSession, type RuntimeSessionHandle, SDK_VERSION, SENSITIVE_ANNOTATION, SESSION_SCHEMA_VERSION, SIGNATURE_SCHEMA_VERSION, SKILLS_LOCKFILE, SKILL_MANIFEST_FILE, SKILL_NAME_MAX_LENGTH, SKILL_NAME_PATTERN, SKILL_PACK_FILE, SKILL_SIGNATURE_FILE, SUPPORTED_DOCUMENT_MIME, type SchemaInferer, type ScriptExecutionContext, type ScriptExecutor, type SealOptions, type SealRecord, type SealResult, SerializingMemoryStore, type SessionListPage, type SessionMeta, type SessionRecord, type SessionStore, type SignatureAuditSink, type SignatureVerdict, type SkillArchiveDetection, type SkillArchiveShape, type SkillCandidateMarker, type SkillCatalog, type SkillCatalogEntry, SkillDiscovery, type SkillDocument, type SkillFailureReport, type SkillInstallSource, type SkillIntegrityGuard, type SkillIssue, type SkillLocation, type SkillManagerPort, type SkillManifest, type SkillMetadata, type SkillOutcomeReporter, type SkillPackManifest, SkillReader, type SkillRouter, type SkillScriptDescriptor, type SkillScriptSchemaSource, type SkillSignature, type SkillSource, type SkillStateGuard, type SkillSuccessReport, type SkillsLockfile, TEXT_BUDGETED_CONTENT_TYPES, TEXT_EXTENSIONS, type TerminalLifecycleData, type TextualToolContent, type TodoTraceEvent, type ToolCallContext, type ToolContent, type ToolDefinition, type ToolDisclosure, type ToolResolution, type ToolResult, type ToolStepReader, type ToolStepRecord, type ToolStepStore, type ToolStepTrust, type TraceClock, type TraceEvent, type TraceEventType, TraceRecorder, type TrustedKey, type TrustedKeyStore, UNSUPPORTED_DOCUMENT_MESSAGE, UNTRUSTED_LINE_LIMIT, UPLOAD_FILES_UNAVAILABLE, USER_PROFILE_EXPORT_VERSION, USER_PROFILE_KEY, USER_PROFILE_NO_INVENTION_RULE, USER_PROFILE_PROMPT_HEADER, USER_PROFILE_REFINE_PROMPT, type UiBridge, type UiSpecActionCapability, type UiSpecDrafts, type UiSpecEvent, type UiSpecNode, type UiSpecPatch, type UiSpecSnapshot, type UiSurfaceActionRequest, type UiSurfaceActionResponse, type UnsignedPolicy, type UnsupportedRunSnapshot, type UploadFileInfo, type UserProfile, type UserProfileEntry, type UserProfileExport, type UserProfileImportDiff, type UserProfileLimits, type ValidationReport, type VercelToolSpec, type VerifyResult, type WebSkillApi, WebSkillError, type WebSkillErrorCode, WebSkillRuntime, type WebSkillRuntimeDeps, XLSX_MIME, type XlsxTextExtractor, appendBehaviorRecords, applyUserProfileImport, assertRemoteUrlAllowed, assertSafePathSegment, atomicWriteText, bridgeError, buildCatalog, buildManifest, buildRenderResult, checkDependencyCycles, checkSkillRules, classifyAttachment, computeDigest, createScriptContext, createWebSkillApi, detectSkillArchiveShape, detectSkillArchiveShapeFromFs, diffUserProfile, escapeXml, exportSkills, exportUserProfile, extractChartSpec, extractSkillCandidate, extractTodoTraceEvents, extractUiSpecEvents, findUnpairedToolCalls, formatAttachmentText, formatSkillScriptManifest, fromVercelResult, fromVercelStreamPart, interruptedToolResult, isAtomicTempPath, isNetworkAllowed, isUnsupportedRunSnapshot, isValidSkillName, jsonRenderer, keyIdOf, listSkillScripts, mergeCatalogEntries, mergeProfileEntries, messageOf, networkPolicyLibSource, networkUrlHost, normalizeErrorCode, normalizePath, normalizeToolContent, normalizeToolError, parseBridgeRequest, parseSkillMarkdown, parseSkillPackManifest, parseUserProfileExport, partsToText, readBehaviorRecords, readProfileEntries, readResponseWithLimit, readSkillSignature, readUserProfile, redactToolStepArgs, refineUserProfile, renderAvailableSkillsXml, renderCatalogJson, renderUserProfileContext, resolveArchiveLimits, resolveInsideRoot, resolveToolName, sampleBehaviorRecords, sanitizeUntrustedLine, schemaSourceLabel, schemaToForm, scriptToolName, sealToolCallPairs, signSkill, signaturePayloadBytes, stripArchiveRoot, summarizeRunUsage, summarizeToolCalls, textParts, toBase64, toLlmToolSpec, toRecordDigests, toVercelToolSpecs, unzipWithLimits, validateSkills, validateUiSpecEvent, validateUiSpecNode, verifyManifest, verifySkillSignature, xmlRenderer };
package/dist/index.js CHANGED
@@ -3,13 +3,13 @@ import { A as SKILL_MANIFEST_FILE, C as keyIdOf, D as verifySkillSignature, E as
3
3
  import { a as atomicWriteText, i as ATOMIC_TMP_SUFFIX_PATTERN, n as normalizePath, o as isAtomicTempPath, r as resolveInsideRoot, t as assertSafePathSegment } from "./pathSecurity-B1owvJAF.js";
4
4
  import { t as assertRemoteUrlAllowed } from "./urlSafety-CiSuCJvX.js";
5
5
  import { a as TEXT_EXTENSIONS, c as formatAttachmentText, i as IMAGE_MIME_TYPES, l as UNTRUSTED_LINE_LIMIT, n as DOCX_MIME, o as XLSX_MIME, r as FILE_MIME_TYPES, s as classifyAttachment, t as ATTACHMENT_TEXT_LIMIT, u as sanitizeUntrustedLine } from "./kind-DxgS8LM-.js";
6
- import { $ as scriptToolName, A as readUserProfile, B as createScriptContext, C as TraceRecorder, D as mergeProfileEntries, E as appendBehaviorRecords, F as DEFAULT_USER_PROFILE_LIMITS, G as ASK_USER_TOOL_NAME, H as ASK_USER_INPUT_SCHEMA, I as EMPTY_USER_PROFILE, J as READ_SKILL_FILE_TOOL_NAME, K as READ_SKILL_FILE_INPUT_SCHEMA, L as SerializingMemoryStore, M as USER_PROFILE_NO_INVENTION_RULE, N as USER_PROFILE_PROMPT_HEADER, O as readBehaviorRecords, P as renderUserProfileContext, Q as schemaSourceLabel, R as EventBus, S as extractTodoTraceEvents, T as USER_PROFILE_KEY, U as ASK_USER_MAX_FIELDS, V as ASK_USER_FIELD_TYPES, W as ASK_USER_TOOL, X as formatSkillScriptManifest, Y as ALLOWED_TOOLS_EXCLUSION_REASON, Z as listSkillScripts, _ as RUN_SNAPSHOT_SCHEMA_VERSION, a as networkPolicyLibSource, at as xmlRenderer, b as MAX_TOOL_STEP_ARG_BYTES, c as bridgeError, d as FsMemoryStore, et as resolveToolName, f as WebSkillRuntime, g as FsRunSnapshotStore, h as redactToolStepArgs, i as isNetworkAllowed, it as renderAvailableSkillsXml, j as sampleBehaviorRecords, k as readProfileEntries, l as parseBridgeRequest, m as SENSITIVE_ANNOTATION, n as normalizeErrorCode, nt as ProgressiveRouter, o as networkUrlHost, p as AgentLoop, q as READ_SKILL_FILE_TOOL, r as normalizeToolError, rt as escapeXml, s as UPLOAD_FILES_UNAVAILABLE, t as CapabilityApproval, tt as toLlmToolSpec, u as FsArtifactStore, v as isUnsupportedRunSnapshot, w as BEHAVIOR_RECORDS_KEY, x as extractSkillCandidate, y as DEFAULT_LOOP_LIMITS, z as schemaToForm } from "./approval-CBPnGPIm.js";
7
- import { a as GoogleGenAiClient, c as findUnpairedToolCalls, d as partsToText, f as promptText, i as toVercelToolSpecs, l as interruptedToolResult, n as fromVercelResult, o as AnthropicClient, p as textParts, r as fromVercelStreamPart, s as OpenAiCompatibleClient, u as sealToolCallPairs } from "./llm-B7lLH0ZI.js";
6
+ import { $ as scriptToolName, A as readUserProfile, B as createScriptContext, C as TraceRecorder, D as mergeProfileEntries, E as appendBehaviorRecords, F as DEFAULT_USER_PROFILE_LIMITS, G as ASK_USER_TOOL_NAME, H as ASK_USER_INPUT_SCHEMA, I as EMPTY_USER_PROFILE, J as READ_SKILL_FILE_TOOL_NAME, K as READ_SKILL_FILE_INPUT_SCHEMA, L as SerializingMemoryStore, M as USER_PROFILE_NO_INVENTION_RULE, N as USER_PROFILE_PROMPT_HEADER, O as readBehaviorRecords, P as renderUserProfileContext, Q as schemaSourceLabel, R as EventBus, S as extractTodoTraceEvents, T as USER_PROFILE_KEY, U as ASK_USER_MAX_FIELDS, V as ASK_USER_FIELD_TYPES, W as ASK_USER_TOOL, X as formatSkillScriptManifest, Y as ALLOWED_TOOLS_EXCLUSION_REASON, Z as listSkillScripts, _ as RUN_SNAPSHOT_SCHEMA_VERSION, a as networkPolicyLibSource, at as xmlRenderer, b as MAX_TOOL_STEP_ARG_BYTES, c as bridgeError, d as FsMemoryStore, et as resolveToolName, f as WebSkillRuntime, g as FsRunSnapshotStore, h as redactToolStepArgs, i as isNetworkAllowed, it as renderAvailableSkillsXml, j as sampleBehaviorRecords, k as readProfileEntries, l as parseBridgeRequest, m as SENSITIVE_ANNOTATION, n as normalizeErrorCode, nt as ProgressiveRouter, o as networkUrlHost, p as AgentLoop, q as READ_SKILL_FILE_TOOL, r as normalizeToolError, rt as escapeXml, s as UPLOAD_FILES_UNAVAILABLE, t as CapabilityApproval, tt as toLlmToolSpec, u as FsArtifactStore, v as isUnsupportedRunSnapshot, w as BEHAVIOR_RECORDS_KEY, x as extractSkillCandidate, y as DEFAULT_LOOP_LIMITS, z as schemaToForm } from "./approval-Bbh_Apwg.js";
7
+ import { a as GoogleGenAiClient, c as findUnpairedToolCalls, d as partsToText, f as promptText, i as toVercelToolSpecs, l as interruptedToolResult, n as fromVercelResult, o as AnthropicClient, p as textParts, r as fromVercelStreamPart, s as OpenAiCompatibleClient, u as sealToolCallPairs } from "./llm-eIQNO9tr.js";
8
8
  import { c as DEFAULT_MAX_DOCUMENT_BYTES, d as TEXT_BUDGETED_CONTENT_TYPES, i as UNSUPPORTED_DOCUMENT_MESSAGE, l as DEFAULT_MAX_DOCUMENT_TEXT_BYTES, n as READ_LINKED_DOCUMENT_TOOL_NAME, o as toBase64, r as SUPPORTED_DOCUMENT_MIME, s as DEFAULT_MAX_DATA_SOURCE_BYTES, t as READ_LINKED_DOCUMENT_TOOL, u as DEFAULT_MAX_UPLOAD_FILE_BYTES } from "./linkedDocument-xNXF-z8n.js";
9
9
  import { n as normalizeToolContent, t as mergeCatalogEntries } from "./external-_ZRQe-V9.js";
10
10
  import { n as extractChartSpec, t as buildRenderResult } from "./renderResult-D9Q-Vu2x.js";
11
11
  import { n as validateUiSpecEvent, r as validateUiSpecNode, t as extractUiSpecEvents } from "./surface-DVGiCmwq.js";
12
- import { t as createWebSkillApi } from "./webSkillApi-Ce2OO5S5.js";
12
+ import { t as createWebSkillApi } from "./webSkillApi-CsvA69qk.js";
13
13
 
14
14
  //#region ../core/src/fs/memoryFs.ts
15
15
  const ROOT = "/";
@@ -1004,7 +1004,7 @@ var FsToolStepStore = class {
1004
1004
  * Version of the published `@webskill/sdk` package, injected at build time.
1005
1005
  * @stable
1006
1006
  */
1007
- const SDK_VERSION = "0.17.0";
1007
+ const SDK_VERSION = "0.19.0";
1008
1008
 
1009
1009
  //#endregion
1010
1010
  export { ALLOWED_TOOLS_EXCLUSION_REASON, ASK_USER_FIELD_TYPES, ASK_USER_INPUT_SCHEMA, ASK_USER_MAX_FIELDS, ASK_USER_TOOL, ASK_USER_TOOL_NAME, ATOMIC_TMP_SUFFIX_PATTERN, ATTACHMENT_TEXT_LIMIT, AgentLoop, AnthropicClient, BEHAVIOR_RECORDS_KEY, CapabilityApproval, DEFAULT_ARCHIVE_LIMITS, DEFAULT_LOOP_LIMITS, DEFAULT_MAX_DATA_SOURCE_BYTES, DEFAULT_MAX_DOCUMENT_BYTES, DEFAULT_MAX_DOCUMENT_TEXT_BYTES, DEFAULT_MAX_UPLOAD_FILE_BYTES, DEFAULT_USER_PROFILE_LIMITS, DOCX_MIME, EMPTY_USER_PROFILE, EventBus, FILE_MIME_TYPES, FS_SESSION_PAGE_SIZE, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FsToolStepStore, FsTrustedKeyStore, FullDisclosureRouter, GoogleGenAiClient, HookRunner, IMAGE_MIME_TYPES, MANIFEST_EXCLUDED_FILES, MAX_TOOL_STEP_ARG_BYTES, MemoryFS, OpenAiCompatibleClient, ProgressiveRouter, READ_LINKED_DOCUMENT_TOOL, READ_LINKED_DOCUMENT_TOOL_NAME, READ_SKILL_FILE_INPUT_SCHEMA, READ_SKILL_FILE_TOOL, READ_SKILL_FILE_TOOL_NAME, RUN_SNAPSHOT_SCHEMA_VERSION, RUN_TRACE_SCHEMA_VERSION, SDK_VERSION, SENSITIVE_ANNOTATION, SESSION_SCHEMA_VERSION, SIGNATURE_SCHEMA_VERSION, SKILLS_LOCKFILE, SKILL_MANIFEST_FILE, SKILL_NAME_MAX_LENGTH, SKILL_NAME_PATTERN, SKILL_PACK_FILE, SKILL_SIGNATURE_FILE, SUPPORTED_DOCUMENT_MIME, SerializingMemoryStore, SkillDiscovery, SkillReader, TEXT_BUDGETED_CONTENT_TYPES, TEXT_EXTENSIONS, TraceRecorder, UNSUPPORTED_DOCUMENT_MESSAGE, UNTRUSTED_LINE_LIMIT, UPLOAD_FILES_UNAVAILABLE, USER_PROFILE_EXPORT_VERSION, USER_PROFILE_KEY, USER_PROFILE_NO_INVENTION_RULE, USER_PROFILE_PROMPT_HEADER, USER_PROFILE_REFINE_PROMPT, WebSkillError, WebSkillRuntime, XLSX_MIME, appendBehaviorRecords, applyUserProfileImport, assertRemoteUrlAllowed, assertSafePathSegment, atomicWriteText, bridgeError, buildCatalog, buildManifest, buildRenderResult, checkDependencyCycles, checkSkillRules, classifyAttachment, computeDigest, createScriptContext, createWebSkillApi, detectSkillArchiveShape, detectSkillArchiveShapeFromFs, diffUserProfile, escapeXml, exportSkills, exportUserProfile, extractChartSpec, extractSkillCandidate, extractTodoTraceEvents, extractUiSpecEvents, findUnpairedToolCalls, formatAttachmentText, formatSkillScriptManifest, fromVercelResult, fromVercelStreamPart, interruptedToolResult, isAtomicTempPath, isNetworkAllowed, isUnsupportedRunSnapshot, isValidSkillName, jsonRenderer, keyIdOf, listSkillScripts, mergeCatalogEntries, mergeProfileEntries, messageOf, networkPolicyLibSource, networkUrlHost, normalizeErrorCode, normalizePath, normalizeToolContent, normalizeToolError, parseBridgeRequest, parseSkillMarkdown, parseSkillPackManifest, parseUserProfileExport, partsToText, readBehaviorRecords, readProfileEntries, readResponseWithLimit, readSkillSignature, readUserProfile, redactToolStepArgs, refineUserProfile, renderAvailableSkillsXml, renderCatalogJson, renderUserProfileContext, resolveArchiveLimits, resolveInsideRoot, resolveToolName, sampleBehaviorRecords, sanitizeUntrustedLine, schemaSourceLabel, schemaToForm, scriptToolName, sealToolCallPairs, signSkill, signaturePayloadBytes, stripArchiveRoot, summarizeRunUsage, summarizeToolCalls, textParts, toBase64, toLlmToolSpec, toRecordDigests, toVercelToolSpecs, unzipWithLimits, validateSkills, validateUiSpecEvent, validateUiSpecNode, verifyManifest, verifySkillSignature, xmlRenderer };
@@ -809,6 +809,44 @@ const toOpenAiTools = (tools) => tools.map((tool) => ({
809
809
  }
810
810
  }));
811
811
  const errorMessage$2 = (e) => e instanceof Error ? e.message : String(e);
812
+ /** 错误正文只给能定位问题的开头一段,整份上游响应仍在 cause 里 */
813
+ const DETAIL_LIMIT = 500;
814
+ /**
815
+ * 2xx 响应体里的业务错误文本。兼容端点在「HTTP 成功但业务失败」时的三种常见形态:
816
+ * OpenAI `{error:{message}}`、Ollama `{error:"..."}`、vLLM `{object:"error",message:"..."}`。
817
+ */
818
+ const upstreamErrorText = (data) => {
819
+ if (typeof data !== "object" || data === null) return void 0;
820
+ const record = data;
821
+ const error = record["error"];
822
+ if (typeof error === "string" && error !== "") return error;
823
+ if (typeof error === "object" && error !== null) {
824
+ const message = error["message"];
825
+ if (typeof message === "string" && message !== "") return message;
826
+ }
827
+ const top = record["message"];
828
+ if (record["object"] === "error" && typeof top === "string" && top !== "") return top;
829
+ };
830
+ const parseJsonOrUndefined = (text) => {
831
+ try {
832
+ return JSON.parse(text);
833
+ } catch {
834
+ return;
835
+ }
836
+ };
837
+ /**
838
+ * 错误正文会进 trace 与控制台,而网关形态的 baseUrl 里常带凭据
839
+ * (`https://user:pass@host`、`?api-key=...`)。报 URL 只为了让宿主认出自己指错了地方,
840
+ * origin + pathname 已经够;解不出来的就原样给回(那就不是一个能藏凭据的 URL)。
841
+ */
842
+ const redactUrl = (url) => {
843
+ try {
844
+ const parsed = new URL(url);
845
+ return `${parsed.origin}${parsed.pathname}`;
846
+ } catch {
847
+ return url;
848
+ }
849
+ };
812
850
  /** OpenAI chat completions 协议客户端(兼容端点通用) */
813
851
  var OpenAiCompatibleClient = class {
814
852
  #config;
@@ -827,19 +865,19 @@ var OpenAiCompatibleClient = class {
827
865
  };
828
866
  }
829
867
  async complete(input) {
830
- const res = await this.#postChat(input, false);
868
+ const { res, endpoint } = await this.#postChat(input, false);
831
869
  let data;
832
870
  try {
833
871
  data = await res.json();
834
872
  } catch (e) {
835
- throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse LLM response: ${errorMessage$2(e)}`, e);
873
+ throw new WebSkillError("LLM_REQUEST_FAILED", `Failed to parse the LLM response from ${endpoint}: ${errorMessage$2(e)}`, e);
836
874
  }
837
- return this.#parseResponse(data);
875
+ return this.#parseResponse(data, endpoint);
838
876
  }
839
877
  /** SSE 流式:fetch + ReadableStream 按行解析 data: 帧(Node/浏览器通用) */
840
878
  async *stream(input) {
841
- const res = await this.#postChat(input, true);
842
- if (!res.body) throw new WebSkillError("LLM_REQUEST_FAILED", "LLM streaming response has no body");
879
+ const { res, endpoint } = await this.#postChat(input, true);
880
+ if (!res.body) throw new WebSkillError("LLM_REQUEST_FAILED", `The LLM streaming response from ${endpoint} has no body`);
843
881
  const toolCallsByIndex = /* @__PURE__ */ new Map();
844
882
  const decoder = new TextDecoder();
845
883
  const reader = res.body.getReader();
@@ -847,6 +885,8 @@ var OpenAiCompatibleClient = class {
847
885
  const think = createThinkTagSplitter();
848
886
  let done = false;
849
887
  let usage;
888
+ let sawFrame = false;
889
+ let head = "";
850
890
  const handleFrame = function* (data) {
851
891
  if (data === "[DONE]") {
852
892
  done = true;
@@ -897,7 +937,10 @@ var OpenAiCompatibleClient = class {
897
937
  try {
898
938
  for (;;) {
899
939
  const { value, done: readerDone } = await reader.read();
900
- const payloads = readerDone ? frames.flush() : frames.push(decoder.decode(value, { stream: true }));
940
+ const text = readerDone ? "" : decoder.decode(value, { stream: true });
941
+ const payloads = readerDone ? frames.flush() : frames.push(text);
942
+ if (payloads.length > 0) sawFrame = true;
943
+ else if (!sawFrame && head.length < DETAIL_LIMIT) head += text;
901
944
  for (const payload of payloads) {
902
945
  yield* handleFrame(payload);
903
946
  if (done) break;
@@ -907,6 +950,10 @@ var OpenAiCompatibleClient = class {
907
950
  } finally {
908
951
  reader.releaseLock();
909
952
  }
953
+ if (!sawFrame) {
954
+ const upstream = upstreamErrorText(parseJsonOrUndefined(head));
955
+ throw new WebSkillError("LLM_REQUEST_FAILED", upstream === void 0 ? `The LLM stream from ${endpoint} carried no data frames; the endpoint answered HTTP 200 but not an event stream${head === "" ? "" : `: ${head.slice(0, DETAIL_LIMIT)}`}` : `The endpoint ${endpoint} answered HTTP 200 with an error instead of a stream: ${upstream.slice(0, DETAIL_LIMIT)}`, head);
956
+ }
910
957
  const tail = think.flush();
911
958
  if (tail.thinking !== "") yield {
912
959
  type: "thinking-delta",
@@ -955,9 +1002,11 @@ var OpenAiCompatibleClient = class {
955
1002
  body["stream"] = true;
956
1003
  body["stream_options"] = { include_usage: true };
957
1004
  }
1005
+ const endpoint = `${baseUrl}/chat/completions`;
1006
+ const shown = redactUrl(endpoint);
958
1007
  let res;
959
1008
  try {
960
- res = await this.#fetch(`${baseUrl}/chat/completions`, {
1009
+ res = await this.#fetch(endpoint, {
961
1010
  method: "POST",
962
1011
  headers: {
963
1012
  "content-type": "application/json",
@@ -967,13 +1016,16 @@ var OpenAiCompatibleClient = class {
967
1016
  signal: input.signal ?? (this.#config.requestTimeoutMs ? AbortSignal.timeout(this.#config.requestTimeoutMs) : null)
968
1017
  });
969
1018
  } catch (e) {
970
- throw new WebSkillError("LLM_REQUEST_FAILED", `LLM request failed: ${errorMessage$2(e)}`, e);
1019
+ throw new WebSkillError("LLM_REQUEST_FAILED", `The LLM request to ${shown} failed: ${errorMessage$2(e)}`, e);
971
1020
  }
972
1021
  if (!res.ok) {
973
1022
  const detail = await res.text().catch(() => "");
974
- throw new WebSkillError("LLM_REQUEST_FAILED", `LLM request failed with HTTP ${res.status}${detail ? `: ${detail.slice(0, 500)}` : ""}`, { status: res.status });
1023
+ throw new WebSkillError("LLM_REQUEST_FAILED", `The LLM request to ${shown} failed with HTTP ${res.status}${detail ? `: ${detail.slice(0, DETAIL_LIMIT)}` : ""}`, { status: res.status });
975
1024
  }
976
- return res;
1025
+ return {
1026
+ res,
1027
+ endpoint: shown
1028
+ };
977
1029
  }
978
1030
  /** 轻量探测(GET /models),集成测试据此决定 skip */
979
1031
  async checkAvailability() {
@@ -984,10 +1036,13 @@ var OpenAiCompatibleClient = class {
984
1036
  return false;
985
1037
  }
986
1038
  }
987
- #parseResponse(data) {
1039
+ #parseResponse(data, endpoint) {
988
1040
  try {
989
1041
  const choice = data.choices?.[0]?.message;
990
- if (!choice) throw new Error("response has no choices[0].message");
1042
+ if (!choice) {
1043
+ const upstream = upstreamErrorText(data);
1044
+ throw new Error(upstream === void 0 ? `the response from ${endpoint} has no choices[0].message` : `the endpoint ${endpoint} answered HTTP 200 with an error instead of a completion: ${upstream.slice(0, DETAIL_LIMIT)}`);
1045
+ }
991
1046
  const toolCalls = choice["tool_calls"]?.map((tc) => {
992
1047
  let args = {};
993
1048
  let parseError;
package/dist/mcp.d.ts CHANGED
@@ -568,4 +568,280 @@ interface RemoteEndpointHandle {
568
568
  */
569
569
  declare function connectRemoteEndpoint(registry: EndpointRegistry<McpClientLike>, config: RemoteEndpointConfig): Promise<RemoteEndpointHandle>;
570
570
  //#endregion
571
- export { type BrowserModelContextLike, EndpointRegistry, ExperimentalWebMcpAdapter, type McpClientLike, type McpOAuthClient, type McpOAuthConfig, type McpOAuthHandshake, type McpOAuthHandshakeStore, type McpOAuthProvider, type McpOAuthStage, type McpOAuthTokenStore, type McpOAuthTokens, McpRuntimePlugin, type McpSourceTrust, type McpToolDisclosure, McpToolResolver, type McpToolVisibility, MessageChannelTransport, type MessageChannelTransportOptions, type MessagePortLike, PAGE_HOST_ANCHOR_KEY, type PageHostAnchor, type RemoteEndpointConfig, type RemoteEndpointHandle, type ServedSkill, TemporarySkillProvider, type TransportState, WEB_MCP_SOURCE_ID_MAX, type WebMcpToolDescriptor, type WebMcpToolLike, catalogMerge, connectRemoteEndpoint, createMemoryOAuthStores, createOAuthProvider, declareDataSourcesToPageHost, endpointToolLlmName, notifyPageHost, parseEndpointToolLlmName, parseWebMcpToolLlmName, serveSkillAsMcp, validateJsonRpcMessage, webMcpToolLlmName };
571
+ //#region src/pageHost/mcpVisibilityStore.d.ts
572
+ /**
573
+ * 启停与披露的状态存储(0.19.0 分册 10 · FR-10.3)。
574
+ *
575
+ * 做成注入点,是因为四份宿主的存量键各不相同:默认实现统一键形之后,
576
+ * 不接受「禁用名单不迁移」代价的接入方可以自己实现一份读旧键的。
577
+ *
578
+ * **WebMCP 工具用保留端点名 `'webmcp'`**,不再走 `webmcp:` 前缀混进同一张名单。
579
+ */
580
+ interface McpVisibilityStore {
581
+ isEndpointEnabled(endpoint: string): boolean;
582
+ setEndpointEnabled(endpoint: string, enabled: boolean): void;
583
+ isToolEnabled(endpoint: string, tool: string): boolean;
584
+ setToolEnabled(endpoint: string, tool: string, enabled: boolean): void;
585
+ toolDisclosure(endpoint: string, tool: string): McpToolDisclosure;
586
+ setToolDisclosure(endpoint: string, tool: string, value: McpToolDisclosure): void;
587
+ isWebMcpEnabled(): boolean;
588
+ setWebMcpEnabled(enabled: boolean): void;
589
+ }
590
+ /** WebMCP 在名单里占用的保留端点名 */
591
+ declare const WEB_MCP_ENDPOINT = "webmcp";
592
+ /**
593
+ * localStorage 默认实现。键形:
594
+ * `<ns>.mcp.endpoints-disabled` / `<ns>.mcp.tools-disabled` /
595
+ * `<ns>.mcp.tools-on-demand` / `<ns>.webmcp-enabled`。
596
+ *
597
+ * 名单里的工具**一律带 endpoint**(`<endpoint>/<tool>`)。旧实现存裸工具名,
598
+ * 两个端点注册同名工具时禁用其一会连坐另一个——那个缺陷在这里一次修完。
599
+ *
600
+ * 一律实时读存储、不做进程内缓存:console 改完当轮生效是既有语义。
601
+ */
602
+ declare function createLocalStorageMcpVisibility(namespace: string): McpVisibilityStore;
603
+ //#endregion
604
+ //#region src/pageHost/createPageMcpEndpoint.d.ts
605
+ /**
606
+ * 进程内 MCP 端点装配(0.19.0 分册 10)。
607
+ *
608
+ * 把页面工具与页面临时技能经**真实 MCP 通道**供给对话运行时:治理面(启停、披露)
609
+ * 因此对 chatbot 与 console 同时生效,而不是两张皮。
610
+ *
611
+ * 这段装配有三处必须写对、宿主各自手写时反复写错的地方,全部收在这里:
612
+ * (a) `MessagePort` 一经转移就归对方所有 → 每次挂载新建 `MessageChannel`;
613
+ * (b) 先建后换 → `registry.set`(不是 `register`),且新通道握手成功后才关旧的;
614
+ * (c) 串行化 → `catch` 必须在 `then` **之前**,否则一次失败会让此后每次重挂被静默跳过。
615
+ */
616
+ /** 页面工具的统一形态:裸返回值由 SDK 裹成 MCP 的 content / structuredContent */
617
+ interface PageMcpTool {
618
+ spec: LlmToolSpec;
619
+ run(args: Record<string, unknown>): Promise<unknown> | unknown;
620
+ }
621
+ type Supplier<T> = readonly T[] | (() => readonly T[]);
622
+ interface PageMcpEndpointOptions {
623
+ /** 端点名(进入 LLM 可见的工具名前缀) */
624
+ endpoint: string;
625
+ /**
626
+ * 工具清单。函数形态用于「清单随宿主状态变化」的场景;
627
+ * 每次 `serve()` 重新求值。
628
+ */
629
+ tools: Supplier<PageMcpTool>;
630
+ /** 当前该端点暴露的临时技能;换屏即换一批,用函数形态 */
631
+ skills?: Supplier<ServedSkill>;
632
+ /** 启停与披露状态;缺省用 `createLocalStorageMcpVisibility(endpoint)` */
633
+ visibility?: McpVisibilityStore;
634
+ /**
635
+ * WebMCP 通道。缺省自建一个解析 `document.modelContext ?? navigator.modelContext`
636
+ * 的适配器;`false` 显式关闭(Node 宿主、无 DOM 环境)。
637
+ */
638
+ webMcp?: ExperimentalWebMcpAdapter | readonly ExperimentalWebMcpAdapter[] | false;
639
+ /**
640
+ * 参数留存的信任声明(分册 30 / FR-30.6)。缺省不声明任何来源可信,
641
+ * 于是本端点每个工具的入参都按 `untrusted` 全量脱敏——由此生成的技能参数为空。
642
+ * 宿主自己写的页面工具若已审查过 inputSchema,显式声明 `isEndpointTrusted` 才留存。
643
+ */
644
+ trust?: McpSourceTrust;
645
+ /** 透传给 `ExternalToolSource.systemPrompt`(已有扩展点,本册不改语义) */
646
+ systemPrompt?: () => Promise<string | undefined>;
647
+ serverInfo?: {
648
+ name?: string;
649
+ version?: string;
650
+ };
651
+ clientInfo?: {
652
+ name?: string;
653
+ version?: string;
654
+ };
655
+ onWarning?: (message: string) => void;
656
+ }
657
+ interface PageMcpEndpoint {
658
+ /** 端点名(门面与 console 都要用它区分内置端点与远程端点) */
659
+ readonly endpoint: string;
660
+ readonly registry: EndpointRegistry<McpClientLike>;
661
+ readonly plugin: McpRuntimePlugin;
662
+ readonly pageSkills: TemporarySkillProvider;
663
+ /** 缺省或宿主注入的 WebMCP 适配器;`webMcp: false` 时为 undefined */
664
+ readonly webmcp: ExperimentalWebMcpAdapter | undefined;
665
+ /** 全部 WebMCP 来源(分册 26:主文档与各嵌入页各算一个,**不合并**) */
666
+ readonly webMcpSources: readonly ExperimentalWebMcpAdapter[];
667
+ /** chatbot adapter 的 pageSkillSource:工具源 + 技能提供者的合体对象 */
668
+ readonly toolSource: ExternalToolSource & ExternalSkillProvider;
669
+ readonly visibility: McpVisibilityStore;
670
+ /**
671
+ * 首次挂载完成。**失败也 resolve**:读 registry 的一侧按现状继续,
672
+ * 不能因为一次挂载失败被永久挂起。
673
+ */
674
+ readonly ready: Promise<void>;
675
+ /** 重新挂载当前工具/技能清单;幂等且串行 */
676
+ serve(): Promise<void>;
677
+ /** 关闭当前通道并从 registry 注销 */
678
+ close(): Promise<void>;
679
+ }
680
+ declare function createPageMcpEndpoint(options: PageMcpEndpointOptions): PageMcpEndpoint;
681
+ //#endregion
682
+ //#region src/pageHost/createConnectFacade.d.ts
683
+ /**
684
+ * console 连接页的门面实现(0.19.0 分册 11)。
685
+ *
686
+ * 这里**不 import `@webskill/console` 的类型**,只声明结构等价的形状:
687
+ * 让 `@webskill/mcp` 依赖 console 会倒转依赖边(console 的 peer 里已经有 sdk)。
688
+ * 代价是 console 给 `ConnectFacade` 加必填方法时这里不会红,因此
689
+ * 「宿主侧真实赋值」的守卫测试是本方案成立的前提,不是可选项(同 0.8.0 D-18)。
690
+ */
691
+ /** 结构等价于 `ConnectEndpointConfig` */
692
+ interface ConnectEndpointConfigShape {
693
+ name: string;
694
+ url: string;
695
+ transport?: 'streamable-http' | 'sse';
696
+ headers?: Record<string, string>;
697
+ allowHttp?: boolean;
698
+ allowPrivateHosts?: boolean;
699
+ oauth?: {
700
+ clientId: string;
701
+ redirectUri: string;
702
+ scopes?: string[];
703
+ };
704
+ enabled?: boolean;
705
+ disabledTools?: string[];
706
+ disclosure?: 'always' | 'on-demand';
707
+ }
708
+ /** 结构等价于 `ConnectEndpointView` */
709
+ interface ConnectEndpointViewShape {
710
+ config: ConnectEndpointConfigShape;
711
+ status: 'connected' | 'failed' | 'unavailable' | 'connecting';
712
+ error?: string;
713
+ toolCount?: number;
714
+ }
715
+ /** 结构等价于 `ConnectToolView` */
716
+ interface ConnectToolViewShape {
717
+ endpoint: string;
718
+ name: string;
719
+ description?: string;
720
+ schemaSummary?: string;
721
+ inputSchema?: unknown;
722
+ origin?: string;
723
+ annotations?: {
724
+ untrustedContentHint?: boolean;
725
+ };
726
+ enabled?: boolean;
727
+ disclosure?: 'always' | 'on-demand';
728
+ }
729
+ /** 结构等价于 `ConnectTemporarySkill` */
730
+ interface ConnectTemporarySkillShape {
731
+ name: string;
732
+ description?: string;
733
+ source?: string;
734
+ origin?: string;
735
+ }
736
+ /** 结构等价于 `ConnectTemporarySkillDetail` */
737
+ interface ConnectTemporarySkillDetailShape {
738
+ name: string;
739
+ description?: string;
740
+ source?: string;
741
+ origin?: string;
742
+ body: string;
743
+ metadata?: Record<string, unknown>;
744
+ }
745
+ /** 结构等价于 `ConnectTestResult` */
746
+ interface ConnectTestResultShape {
747
+ ok: boolean;
748
+ latencyMs: number;
749
+ error?: string;
750
+ detail?: string;
751
+ checkedAt?: string;
752
+ }
753
+ /** 结构等价于 `ConnectWebMcpSource` */
754
+ interface ConnectWebMcpSourceShape {
755
+ id?: string;
756
+ label?: string;
757
+ isAvailable(): boolean;
758
+ isEnabled(): boolean;
759
+ setEnabled(on: boolean): void;
760
+ listTools(): Promise<ConnectToolViewShape[]>;
761
+ setToolEnabled?(tool: string, enabled: boolean): void;
762
+ setToolDisclosure?(tool: string, disclosure: 'always' | 'on-demand'): void;
763
+ }
764
+ /** 结构等价于 `ConnectFacade` */
765
+ interface ConnectFacadeShape {
766
+ listEndpoints(): Promise<ConnectEndpointViewShape[]>;
767
+ addEndpoint(config: ConnectEndpointConfigShape): Promise<void>;
768
+ removeEndpoint(name: string): Promise<void>;
769
+ reconnect(name: string): Promise<void>;
770
+ listTools(): Promise<ConnectToolViewShape[]>;
771
+ testEndpoint(name: string): Promise<ConnectTestResultShape>;
772
+ temporarySkills(): Promise<ConnectTemporarySkillShape[]>;
773
+ temporarySkillDetail?(name: string, origin?: string): Promise<ConnectTemporarySkillDetailShape>;
774
+ pagePerception?(): Promise<ConnectPerceptionViewShape>;
775
+ pageActionConsents?(): Promise<readonly ConnectPageActionConsentViewShape[]>;
776
+ forgetPageActionConsent?(id: string): Promise<void>;
777
+ forgetPageActionConsentScope?(scope: string): Promise<void>;
778
+ setEndpointEnabled?(name: string, enabled: boolean): Promise<void>;
779
+ setToolEnabled?(endpoint: string, tool: string, enabled: boolean): Promise<void>;
780
+ setToolDisclosure?(endpoint: string, tool: string, disclosure: 'always' | 'on-demand'): Promise<void>;
781
+ webmcp: ConnectWebMcpSourceShape & {
782
+ sources?: readonly ConnectWebMcpSourceShape[];
783
+ };
784
+ }
785
+ /** 结构等价于 `ConnectPerceptionView` */
786
+ interface ConnectPerceptionViewShape {
787
+ enabled: boolean;
788
+ include: readonly string[];
789
+ exclude?: readonly string[];
790
+ records: readonly {
791
+ at: string;
792
+ include: readonly string[];
793
+ exclude?: readonly string[];
794
+ nodeCount: number;
795
+ imageCount?: number;
796
+ imagesOmitted?: number;
797
+ imageFailures?: number;
798
+ }[];
799
+ actionScope?: {
800
+ include: readonly string[];
801
+ exclude?: readonly string[];
802
+ };
803
+ }
804
+ /** 结构等价于 `ConnectPageActionConsentView` */
805
+ interface ConnectPageActionConsentViewShape {
806
+ id: string;
807
+ scope: string;
808
+ action: string;
809
+ grantedAt?: string;
810
+ }
811
+ /** 远程端点配置的持久化端口 */
812
+ interface RemoteEndpointStore {
813
+ load(): ConnectEndpointConfigShape[];
814
+ save(configs: ConnectEndpointConfigShape[]): void;
815
+ }
816
+ interface PageActionConsentStore {
817
+ list(): Promise<readonly ConnectPageActionConsentViewShape[]>;
818
+ forget(id: string): Promise<void>;
819
+ forgetScope(scope: string): Promise<void>;
820
+ }
821
+ interface ConnectFacadeOptions {
822
+ /** 分册 10 的端点句柄 */
823
+ host: PageMcpEndpoint;
824
+ /** 内置端点在连接页显示的 url 串;缺省 `in-process://<endpoint>` */
825
+ builtinEndpointUrl?: string;
826
+ /**
827
+ * 远程端点存储。`false` = 这个宿主不支持手工加端点(浏览器扩展:端点由页面给),
828
+ * 此时 add/remove 抛说明式的 `TOOL_UNSUPPORTED`,而不是给个能点但必然失败的按钮。
829
+ */
830
+ remoteEndpoints?: RemoteEndpointStore | false;
831
+ /**
832
+ * 已**声明**的临时技能(不是当前服务状态)。技能随屏生灭,
833
+ * 只反映当前活跃端点会让用户切屏后看到空列表、以为技能丢了。
834
+ * 不注入时回落到 `host.pageSkills.listSkills()`——那是服务状态,语义不同。
835
+ */
836
+ temporarySkills?: () => Promise<ConnectTemporarySkillShape[]> | ConnectTemporarySkillShape[];
837
+ temporarySkillDetail?: (name: string, origin?: string) => Promise<ConnectTemporarySkillDetailShape> | ConnectTemporarySkillDetailShape;
838
+ /** 页面只读感知视图;不注入时 `pagePerception` 方法整体缺席 */
839
+ pagePerception?: () => Promise<ConnectPerceptionViewShape> | ConnectPerceptionViewShape;
840
+ /** 页面操作授权存储;不注入时三个 consent 方法整体缺席 */
841
+ pageActionConsent?: PageActionConsentStore;
842
+ }
843
+ /** localStorage 版远程端点存储(键 `<ns>.mcp.remote-endpoints`) */
844
+ declare function createLocalStorageRemoteEndpoints(namespace: string): RemoteEndpointStore;
845
+ declare function createConnectFacade(options: ConnectFacadeOptions): ConnectFacadeShape;
846
+ //#endregion
847
+ export { type BrowserModelContextLike, type ConnectEndpointConfigShape, type ConnectEndpointViewShape, type ConnectFacadeOptions, type ConnectFacadeShape, type ConnectPageActionConsentViewShape, type ConnectPerceptionViewShape, type ConnectTemporarySkillDetailShape, type ConnectTemporarySkillShape, type ConnectTestResultShape, type ConnectToolViewShape, type ConnectWebMcpSourceShape, EndpointRegistry, ExperimentalWebMcpAdapter, type McpClientLike, type McpOAuthClient, type McpOAuthConfig, type McpOAuthHandshake, type McpOAuthHandshakeStore, type McpOAuthProvider, type McpOAuthStage, type McpOAuthTokenStore, type McpOAuthTokens, McpRuntimePlugin, type McpSourceTrust, type McpToolDisclosure, McpToolResolver, type McpToolVisibility, type McpVisibilityStore, MessageChannelTransport, type MessageChannelTransportOptions, type MessagePortLike, PAGE_HOST_ANCHOR_KEY, type PageActionConsentStore, type PageHostAnchor, type PageMcpEndpoint, type PageMcpEndpointOptions, type PageMcpTool, type RemoteEndpointConfig, type RemoteEndpointHandle, type RemoteEndpointStore, type ServedSkill, TemporarySkillProvider, type TransportState, WEB_MCP_ENDPOINT, WEB_MCP_SOURCE_ID_MAX, type WebMcpToolDescriptor, type WebMcpToolLike, catalogMerge, connectRemoteEndpoint, createConnectFacade, createLocalStorageMcpVisibility, createLocalStorageRemoteEndpoints, createMemoryOAuthStores, createOAuthProvider, createPageMcpEndpoint, declareDataSourcesToPageHost, endpointToolLlmName, notifyPageHost, parseEndpointToolLlmName, parseWebMcpToolLlmName, serveSkillAsMcp, validateJsonRpcMessage, webMcpToolLlmName };