@herbertgao/pi-extensions 2026.9.13 → 2026.9.14

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 (61) hide show
  1. package/node_modules/@herbertgao/pi-bark/package.json +2 -2
  2. package/node_modules/@herbertgao/pi-cc-extensions/package.json +4 -4
  3. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  4. package/node_modules/@herbertgao/pi-subagents/package.json +4 -4
  5. package/node_modules/@herbertgao/resume-from/package.json +2 -2
  6. package/node_modules/@herbertgao/sol-pi/package.json +5 -5
  7. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +2 -0
  8. package/node_modules/pi-mcp-adapter/CHANGELOG.md +26 -0
  9. package/node_modules/pi-mcp-adapter/README.md +39 -9
  10. package/node_modules/pi-mcp-adapter/cli.js +33 -22
  11. package/node_modules/pi-mcp-adapter/commands.ts +3 -3
  12. package/node_modules/pi-mcp-adapter/config.ts +9 -1
  13. package/node_modules/pi-mcp-adapter/dist/config.js +9 -1
  14. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
  15. package/node_modules/pi-mcp-adapter/dist/jev-client.d.ts +1 -2
  16. package/node_modules/pi-mcp-adapter/dist/jev-client.js +59 -32
  17. package/node_modules/pi-mcp-adapter/dist/jev-client.js.map +1 -1
  18. package/node_modules/pi-mcp-adapter/dist/jev-contracts.d.ts +1 -1
  19. package/node_modules/pi-mcp-adapter/dist/jev-key-store.d.ts +33 -4
  20. package/node_modules/pi-mcp-adapter/dist/jev-key-store.js +148 -28
  21. package/node_modules/pi-mcp-adapter/dist/jev-key-store.js.map +1 -1
  22. package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js +1 -2
  23. package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js.map +1 -1
  24. package/node_modules/pi-mcp-adapter/dist/types.d.ts +7 -1
  25. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
  26. package/node_modules/pi-mcp-adapter/index.ts +51 -38
  27. package/node_modules/pi-mcp-adapter/jev-client.ts +58 -31
  28. package/node_modules/pi-mcp-adapter/jev-contracts.ts +2 -2
  29. package/node_modules/pi-mcp-adapter/jev-key-store.ts +144 -31
  30. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  31. package/node_modules/pi-mcp-adapter/mcp-code.ts +6 -6
  32. package/node_modules/pi-mcp-adapter/package.json +5 -5
  33. package/node_modules/pi-mcp-adapter/semantic-search.ts +3 -3
  34. package/node_modules/pi-mcp-adapter/types.ts +7 -1
  35. package/node_modules/pi-multi-account/CHANGELOG.md +6 -0
  36. package/node_modules/pi-multi-account/README.md +6 -5
  37. package/node_modules/pi-multi-account/index.ts +1 -1
  38. package/node_modules/pi-multi-account/package.json +3 -3
  39. package/node_modules/pi-typesafe/dist/backends.d.ts +10 -0
  40. package/node_modules/pi-typesafe/dist/backends.js +26 -0
  41. package/node_modules/pi-typesafe/dist/client.d.ts +1 -1
  42. package/node_modules/pi-typesafe/dist/client.js +10 -6
  43. package/node_modules/pi-typesafe/dist/errors.d.ts +10 -2
  44. package/node_modules/pi-typesafe/dist/errors.js +28 -7
  45. package/node_modules/pi-typesafe/dist/extension.js +2 -1
  46. package/node_modules/pi-typesafe/package.json +4 -4
  47. package/node_modules/pi-web-access/CHANGELOG.md +22 -0
  48. package/node_modules/pi-web-access/README.md +21 -8
  49. package/node_modules/pi-web-access/baizhi.ts +124 -0
  50. package/node_modules/pi-web-access/crawl4ai.ts +2 -10
  51. package/node_modules/pi-web-access/curator-page.ts +3 -1
  52. package/node_modules/pi-web-access/curator-server.ts +1 -0
  53. package/node_modules/pi-web-access/dist/index.js +540 -220
  54. package/node_modules/pi-web-access/firecrawl.ts +2 -10
  55. package/node_modules/pi-web-access/gemini-search.ts +5 -10
  56. package/node_modules/pi-web-access/index.ts +134 -23
  57. package/node_modules/pi-web-access/package.json +5 -1
  58. package/node_modules/pi-web-access/storage.ts +1 -0
  59. package/node_modules/pi-web-access/tool-activation.ts +134 -0
  60. package/node_modules/pi-web-access/utils.ts +9 -1
  61. package/package.json +13 -13
@@ -56,6 +56,18 @@ const INIT_WAIT_TIMEOUT_MS = 30_000;
56
56
  const INIT_FAILURE_MESSAGE_MAX_CHARS = 1_000;
57
57
  const INIT_WAIT_TIMED_OUT: unique symbol = Symbol("init-wait-timed-out");
58
58
 
59
+ function hasEnabledServerWithoutValidMetadata(
60
+ config: McpConfig,
61
+ cache: MetadataCache | null,
62
+ directSpecs: readonly DirectToolSpec[] = [],
63
+ ): boolean {
64
+ return Object.entries(config.mcpServers).some(([serverName, definition]) => {
65
+ if (isServerDisabled(definition) || directSpecs.some((spec) => spec.serverName === serverName)) return false;
66
+ const entry = cache?.servers[serverName];
67
+ return entry === undefined || !isServerCacheValid(entry, definition);
68
+ });
69
+ }
70
+
59
71
  export interface McpServerRegistration {
60
72
  dispose(): Promise<void>;
61
73
  }
@@ -320,17 +332,8 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
320
332
  const envRaw = process.env.MCP_DIRECT_TOOLS;
321
333
  const envDirectToolOverride = parseEnvDirectToolOverride(envRaw);
322
334
  const namespaceEnvOverride = resolveNamespaceEnvOverride(envRaw, envDirectToolOverride);
323
- const enabledEarlyServers = Object.entries(earlyConfig.mcpServers)
324
- .filter(([, definition]) => !isServerDisabled(definition));
325
- const hasStartupServer = enabledEarlyServers.some(([, definition]) =>
326
- definition.lifecycle === "eager" || definition.lifecycle === "keep-alive");
327
- const hasUsableCachedMetadata = earlyCache !== null && enabledEarlyServers.every(([serverName, definition]) => {
328
- const entry = earlyCache.servers[serverName];
329
- return entry !== undefined && isServerCacheValid(entry, definition);
330
- });
331
- const hasColdEnvironmentDirectTools = envRaw !== undefined && envRaw !== "__none__"
332
- && getMissingConfiguredDirectToolServers(earlyConfig, earlyCache, envDirectToolOverride).length > 0;
333
- const deferSessionRuntime = !hasStartupServer && hasUsableCachedMetadata && !hasColdEnvironmentDirectTools;
335
+ const hasStartupServer = Object.values(earlyConfig.mcpServers).some((definition) =>
336
+ !isServerDisabled(definition) && (definition.lifecycle === "eager" || definition.lifecycle === "keep-alive"));
334
337
  const registeredDirectTools = new Map<string, string>();
335
338
  const registeredDirectToolServers = new Map<string, string>();
336
339
  const registeredDirectToolVersions = new Map<string, number>();
@@ -341,8 +344,6 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
341
344
  const fallbackDeactivatedTools = new Set<string>();
342
345
  // directTools: "search" — registered inactive, activated by mcp({ search }).
343
346
  const lazyDirectTools = new Set<string>();
344
- // The lazy tools a search has activated; every other lazy tool is held out
345
- // of the active set. Per process: nothing here survives a restart.
346
347
  const searchActivatedTools = new Set<string>();
347
348
  const toolRenderOptions = resolveMcpToolRenderOptions(earlyConfig.settings);
348
349
  const toolRenderShell = toolRenderOptions.resultRendering === "compact" ? "self" : "default";
@@ -657,29 +658,20 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
657
658
 
658
659
  function getDeferredSessionSnapshot(cwd: string | undefined): {
659
660
  config: McpConfig;
660
- specs: DirectToolSpec[];
661
+ cache: MetadataCache | null;
661
662
  enabledServerCount: number;
662
663
  } | undefined {
663
664
  const config = programmaticConfig
664
665
  ? resolveConfiguredClaudePluginMcp(cloneMcpConfig(sessionConfig), cwd ?? process.cwd())
665
666
  : loadMcpConfig(earlyConfigPath, cwd);
666
667
  const cache = loadMetadataCache();
667
- const enabledServers = Object.entries(config.mcpServers)
668
- .filter(([, definition]) => !isServerDisabled(definition));
669
- if (enabledServers.some(([, definition]) => definition.lifecycle === "eager" || definition.lifecycle === "keep-alive")) {
670
- return undefined;
671
- }
672
- if (!cache || !enabledServers.every(([serverName, definition]) => {
673
- const entry = cache.servers[serverName];
674
- return entry !== undefined && isServerCacheValid(entry, definition);
675
- })) return undefined;
668
+ const enabledServers = Object.values(config.mcpServers).filter((definition) => !isServerDisabled(definition));
669
+ if (enabledServers.some((definition) => definition.lifecycle === "eager" || definition.lifecycle === "keep-alive")) return undefined;
676
670
  if (envRaw !== undefined && envRaw !== "__none__"
677
671
  && getMissingConfiguredDirectToolServers(config, cache, envDirectToolOverride).length > 0) return undefined;
678
- return {
679
- config,
680
- specs: resolveCurrentDirectTools(config, cache),
681
- enabledServerCount: enabledServers.length,
682
- };
672
+ if (config.settings?.deferWithMissingMetadata !== true
673
+ && (cache === null || hasEnabledServerWithoutValidMetadata(config, cache))) return undefined;
674
+ return { config, cache, enabledServerCount: enabledServers.length };
683
675
  }
684
676
 
685
677
  function syncNamespaceTools(
@@ -742,8 +734,6 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
742
734
  .flatMap(([, prompts]) => prompts));
743
735
  }
744
736
 
745
- registerPromptCommands(resolveCachedPrompts(earlyConfig));
746
-
747
737
  const registerRuntimeServer = (name: string, definition: ServerEntry): McpServerRegistration => {
748
738
  if (typeof name !== "string" || name.trim() === "") {
749
739
  throw new Error("MCP server name must be a non-empty string");
@@ -1081,6 +1071,9 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
1081
1071
  });
1082
1072
 
1083
1073
  pi.on("session_start", async (_event, ctx) => {
1074
+ // Reset before any await so replacement sessions cannot inherit activation.
1075
+ searchActivatedTools.clear();
1076
+ holdLazyToolsInactive();
1084
1077
  const generation = ++lifecycleGeneration;
1085
1078
  largeDirectToolsAdvisoryDelivered = false;
1086
1079
  const previousState = state;
@@ -1111,15 +1104,24 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
1111
1104
  if (state) return;
1112
1105
 
1113
1106
  if (!initPromise) {
1114
- const deferredSnapshot = deferSessionRuntime ? getDeferredSessionSnapshot(ctx.cwd) : undefined;
1107
+ const deferredSnapshot = getDeferredSessionSnapshot(ctx.cwd);
1115
1108
  if (deferredSnapshot) {
1116
- deliverLargeDirectToolsAdvisory(ctx, deferredSnapshot.config, deferredSnapshot.specs);
1109
+ const { config, cache, enabledServerCount } = deferredSnapshot;
1110
+ const directResult = syncDirectTools(config, cache);
1111
+ syncProxyTool(config, cache, directResult.specs);
1112
+ syncNamespaceTools(config, cache, directResult.reservedDirectNames, directResult.activeDirectNames);
1113
+ // Pi cannot unregister commands. Wait until cwd is authoritative, and
1114
+ // under the opt-in wait for live metadata, before exposing prompts.
1115
+ if (config.settings?.deferWithMissingMetadata !== true) {
1116
+ registerPromptCommands(resolveCachedPrompts(config));
1117
+ }
1118
+ deliverLargeDirectToolsAdvisory(ctx, config, directResult.specs);
1117
1119
  if (generation !== lifecycleGeneration || !owner.isActive() || currentOwner !== owner) return;
1118
- const serverCount = Object.keys(deferredSnapshot.config.mcpServers).length;
1120
+ const serverCount = Object.keys(config.mcpServers).length;
1119
1121
  const formattedStatus = formatMcpFooterStatus(
1120
- deferredSnapshot.config,
1121
- deferredSnapshot.enabledServerCount,
1122
- serverCount - deferredSnapshot.enabledServerCount,
1122
+ config,
1123
+ enabledServerCount,
1124
+ serverCount - enabledServerCount,
1123
1125
  0,
1124
1126
  );
1125
1127
  const theme = ctx.ui?.theme;
@@ -1148,6 +1150,9 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
1148
1150
  await initializationStarted;
1149
1151
  });
1150
1152
 
1153
+ // Other extensions can reactivate registered tools after session_start.
1154
+ pi.on("before_agent_start", holdLazyToolsInactive);
1155
+
1151
1156
  pi.on("session_tree", (_event, ctx) => {
1152
1157
  const currentState = state;
1153
1158
  const owner = currentOwner;
@@ -1589,6 +1594,12 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
1589
1594
  details: { mode: "install", error: "programmatic_config" },
1590
1595
  };
1591
1596
  }
1597
+ if (targetState.config.settings?.allowInstall === false) {
1598
+ return {
1599
+ content: [{ type: "text" as const, text: "MCP install is disabled by configuration." }],
1600
+ details: { mode: "install", error: "install_disabled" },
1601
+ };
1602
+ }
1592
1603
  if (target !== undefined && target !== "global" && target !== "project") {
1593
1604
  return {
1594
1605
  content: [{ type: "text" as const, text: "MCP install target must be 'global' or 'project'." }],
@@ -1787,7 +1798,7 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
1787
1798
  describe: Type.Optional(Type.String({ description: "Tool name to describe (shows parameters)" })),
1788
1799
  instructions: Type.Optional(Type.String({ description: "Server name to show that server's usage instructions" })),
1789
1800
  search: Type.Optional(Type.String({ description: "Search tools by name/description" })),
1790
- searchMode: Type.Optional(Type.String({ enum: ["lexical", "semantic"], description: "Search backend (default: lexical; semantic is available when a TypeSafe key is configured)" })),
1801
+ searchMode: Type.Optional(Type.String({ enum: ["lexical", "semantic"], description: "Search backend (default: lexical; semantic is available when a System One key is configured)" })),
1791
1802
  regex: Type.Optional(Type.Boolean({ description: "Treat search as regex (default: substring match)" })),
1792
1803
  includeSchemas: Type.Optional(Type.Boolean({ description: "Include parameter schemas in search results (default: true)" })),
1793
1804
  limit: optionalNumber({ minimum: 1, description: "Maximum search results to return (default: 12)" }),
@@ -1941,6 +1952,7 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
1941
1952
  }
1942
1953
  if (params.search !== undefined) {
1943
1954
  const result = await proxyModes.executeSearch(proxyState, params.search, params.regex, params.server, params.includeSchemas, params.limit, params.offset, params.searchMode, signal);
1955
+ assertRuntimeGuard(proxyGuard);
1944
1956
  if (lazyDirectTools.size === 0) return result;
1945
1957
  holdLazyToolsInactive();
1946
1958
  const matches = (result.details as { matches?: Array<{ server: string; tool: string }> } | undefined)?.matches ?? [];
@@ -1978,7 +1990,8 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
1978
1990
  config.settings?.disableProxyTool !== true
1979
1991
  || directSpecs.length === 0
1980
1992
  || hasSearchModeSpecs
1981
- || missingConfiguredDirectToolServers.length > 0;
1993
+ || missingConfiguredDirectToolServers.length > 0
1994
+ || hasEnabledServerWithoutValidMetadata(config, cache, directSpecs);
1982
1995
 
1983
1996
  if (shouldRegisterProxyTool) {
1984
1997
  const description = buildProxyDescription(config);
@@ -2,10 +2,9 @@ import { TypeSafeClient } from "@typesafe-ai/sdk";
2
2
  import type { McpExtensionState } from "./state.ts";
3
3
  import { isServerDisabled } from "./types.ts";
4
4
  import { combineAbortSignals } from "./runtime-owner.ts";
5
- import { resolveJevCredential, TYPESAFE_API_ORIGIN, type JevCredentialResolution } from "./jev-key-store.ts";
5
+ import { JEV_SDK_PATH, resolveJevCredential, resolveJevEndpoint, type JevCredentialResolution, type ResolvedJevEndpoint } from "./jev-key-store.ts";
6
6
  import type { JevAnswer, JevBudget, JevEvaluateInput, JevEvaluationData, JevEvaluationEnvelope, JevJson, JevQuestion, ResolvedJevSettings } from "./jev-contracts.ts";
7
7
 
8
- export { TYPESAFE_API_ORIGIN };
9
8
  export type { ResolvedJevSettings } from "./jev-contracts.ts";
10
9
 
11
10
  const DEFAULTS: ResolvedJevSettings = {
@@ -18,18 +17,28 @@ const DEFAULTS: ResolvedJevSettings = {
18
17
  const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
19
18
 
20
19
  type JevSettingsInput = Partial<ResolvedJevSettings>;
21
- type Host = { client: TypeSafeClient };
20
+ type Host = { endpoint: ResolvedJevEndpoint; client: TypeSafeClient };
21
+ type CredentialCacheEntry = { endpoint: string; credential: Extract<JevCredentialResolution, { status: "present" }> };
22
22
  const hosts = new WeakMap<McpExtensionState, Host>();
23
- const credentials = new WeakMap<McpExtensionState, Extract<JevCredentialResolution, { status: "present" }>>();
23
+ const credentials = new WeakMap<McpExtensionState, CredentialCacheEntry>();
24
24
 
25
- function resolveCredential(state: McpExtensionState): JevCredentialResolution {
25
+ /**
26
+ * Cached per endpoint so a changed `SYSTEMONE_ENDPOINT` cannot reuse another provider's credential. Callers
27
+ * re-resolve the endpoint on every operation so a switched or newly invalid endpoint takes effect immediately.
28
+ */
29
+ function resolveCredential(state: McpExtensionState, endpoint: ResolvedJevEndpoint): JevCredentialResolution {
26
30
  const existing = credentials.get(state);
27
- if (existing) return existing;
28
- const credential = resolveJevCredential();
29
- if (credential.status === "present") credentials.set(state, credential);
31
+ if (existing && existing.endpoint === endpoint.href) return existing.credential;
32
+ const credential = resolveJevCredential(process.env, endpoint);
33
+ if (credential.status === "present") credentials.set(state, { endpoint: endpoint.href, credential });
30
34
  return credential;
31
35
  }
32
36
 
37
+ function credentialForState(state: McpExtensionState): JevCredentialResolution {
38
+ const resolution = resolveJevEndpoint();
39
+ return resolution.status === "unavailable" ? resolution : resolveCredential(state, resolution.endpoint);
40
+ }
41
+
33
42
  export function areJevSourcesAllowed(state: McpExtensionState, settings: ResolvedJevSettings, sources: Iterable<string>): boolean {
34
43
  for (const source of sources) {
35
44
  const server = state.config.mcpServers[source];
@@ -90,7 +99,7 @@ export function resolveSemanticJevSettings(
90
99
  const configured = state.config.settings?.jev;
91
100
  const settings = validateJevSettings(configured);
92
101
  if (configured === false || configured?.semanticSearch === false) return settings;
93
- const semanticSearch = settings.semanticSearch || (credentialResolver ? credentialResolver() : resolveCredential(state)).status === "present";
102
+ const semanticSearch = settings.semanticSearch || (credentialResolver ? credentialResolver() : credentialForState(state)).status === "present";
94
103
  const hasExplicitAllowlist = configured !== undefined && Object.hasOwn(configured, "allowedServers");
95
104
  const allowedServers = hasExplicitAllowlist
96
105
  ? settings.allowedServers
@@ -219,55 +228,73 @@ function validateResponse(value: unknown, input: JevEvaluateInput): JevEvaluatio
219
228
  return { answers: validated, model: response.model, usage: { inputTokens: safeNumber(usage.input_tokens, "input tokens", true), outputTokens: safeNumber(usage.output_tokens, "output tokens", true) } };
220
229
  }
221
230
 
222
- function fixedOriginFetch(input: string, init?: RequestInit): Promise<Response> {
223
- const url = new URL(input);
224
- if (url.origin !== TYPESAFE_API_ORIGIN) throw new Error("TypeSafe request origin rejected");
225
- return fetch(input, { ...init, redirect: "error" });
231
+ /**
232
+ * Pins every request to the configured endpoint: the SDK appends its own fixed path to the base URL, so this
233
+ * rewrites that path onto the configured one and then rejects anything that does not match the endpoint exactly.
234
+ * Redirects stay refused, so a provider cannot bounce a request carrying the API key somewhere else.
235
+ */
236
+ function createPinnedEndpointFetch(endpoint: ResolvedJevEndpoint): (input: string, init?: RequestInit) => Promise<Response> {
237
+ return (input, init) => {
238
+ // A replacer function keeps `$&`, `$$`, and friends in the configured path literal.
239
+ const target = input.replace(JEV_SDK_PATH, () => endpoint.path);
240
+ const url = new URL(target);
241
+ if (url.origin !== endpoint.origin || url.pathname !== endpoint.path || url.search !== "" || url.hash !== "") {
242
+ throw new Error("Jev request endpoint rejected");
243
+ }
244
+ return fetch(target, { ...init, redirect: "error" });
245
+ };
226
246
  }
227
247
  function getHost(state: McpExtensionState, settings: ResolvedJevSettings): Host | JevEvaluationEnvelope {
248
+ const resolution = resolveJevEndpoint();
249
+ if (resolution.status === "unavailable") return { ok: false, error: { code: "endpoint_unavailable", message: resolution.message } };
250
+ const { endpoint } = resolution;
228
251
  const existing = hosts.get(state);
229
- if (existing) return existing;
230
- const credential = resolveCredential(state);
231
- if (credential.status === "missing") return { ok: false, error: { code: "credential_missing", message: "TypeSafe API key is not configured." } };
252
+ if (existing && existing.endpoint.href === endpoint.href) return existing;
253
+ const credential = resolveCredential(state, endpoint);
254
+ if (credential.status === "missing") return { ok: false, error: { code: "credential_missing", message: "Jev API key is not configured." } };
232
255
  if (credential.status === "unavailable") return { ok: false, error: { code: "credential_unavailable", message: credential.message } };
233
- const host = { client: new TypeSafeClient({ apiKey: credential.apiKey, baseURL: TYPESAFE_API_ORIGIN, defaultModel: settings.model, logLevel: "off", retry: { maxRetries: settings.maxRetries }, timeout: settings.requestTimeoutMs, defaultHeaders: {}, fetch: fixedOriginFetch }) };
256
+ const host: Host = { endpoint, client: new TypeSafeClient({ apiKey: credential.apiKey, baseURL: endpoint.origin, defaultModel: settings.model, logLevel: "off", retry: { maxRetries: settings.maxRetries }, timeout: settings.requestTimeoutMs, defaultHeaders: {}, fetch: createPinnedEndpointFetch(endpoint) }) };
234
257
  hosts.set(state, host);
235
258
  return host;
236
259
  }
237
260
  function failure(error: unknown, signal: AbortSignal | undefined, timedOut: boolean): JevEvaluationEnvelope {
238
- if (timedOut) return { ok: false, error: { code: "timeout", message: "TypeSafe evaluation timed out.", retryable: true } };
239
- if (signal?.aborted || (error instanceof Error && error.name === "APIUserAbortError")) return { ok: false, error: { code: "aborted", message: "TypeSafe evaluation was aborted." } };
261
+ if (timedOut) return { ok: false, error: { code: "timeout", message: "Jev evaluation timed out.", retryable: true } };
262
+ if (signal?.aborted || (error instanceof Error && error.name === "APIUserAbortError")) return { ok: false, error: { code: "aborted", message: "Jev evaluation was aborted." } };
263
+ // Provider and gateway bodies are never echoed; only the status is used to classify the failure.
240
264
  const status = typeof error === "object" && error !== null && "status" in error ? (error as { status?: unknown }).status : undefined;
241
- if (status === 401 || status === 403) return { ok: false, error: { code: "authentication_failed", message: "TypeSafe authentication failed." } };
242
- if (status === 408) return { ok: false, error: { code: "timeout", message: "TypeSafe evaluation timed out.", retryable: true } };
243
- if (status === 429) return { ok: false, error: { code: "rate_limited", message: "TypeSafe rate limit exceeded.", retryable: true } };
244
- if (typeof status === "number" && status >= 500) return { ok: false, error: { code: "service_unavailable", message: "TypeSafe service is unavailable.", retryable: true } };
245
- if (status === 400 || status === 422) return { ok: false, error: { code: "invalid_request", message: "TypeSafe rejected the evaluation request." } };
246
- if (error instanceof Error && (error.name === "APIConnectionError" || error.name === "APITimeoutError")) return { ok: false, error: { code: "service_unavailable", message: "TypeSafe service is unavailable.", retryable: true } };
247
- return { ok: false, error: { code: "invalid_response", message: "TypeSafe returned an invalid response." } };
265
+ if (status === 401 || status === 403) return { ok: false, error: { code: "authentication_failed", message: "Jev authentication failed." } };
266
+ if (status === 402) return { ok: false, error: { code: "payment_required", message: "Jev provider requires payment for this account." } };
267
+ // A configurable endpoint makes a wrong path a configuration error, not a malformed response.
268
+ if (status === 404 || status === 405 || status === 410) return { ok: false, error: { code: "endpoint_unavailable", message: "Jev endpoint was not found; check SYSTEMONE_ENDPOINT." } };
269
+ if (status === 408) return { ok: false, error: { code: "timeout", message: "Jev evaluation timed out.", retryable: true } };
270
+ if (status === 429) return { ok: false, error: { code: "rate_limited", message: "Jev rate limit exceeded.", retryable: true } };
271
+ if (typeof status === "number" && status >= 500) return { ok: false, error: { code: "service_unavailable", message: "Jev service is unavailable.", retryable: true } };
272
+ if (typeof status === "number" && status >= 400) return { ok: false, error: { code: "invalid_request", message: `Jev rejected the evaluation request (HTTP ${status}).` } };
273
+ if (error instanceof Error && (error.name === "APIConnectionError" || error.name === "APITimeoutError")) return { ok: false, error: { code: "service_unavailable", message: "Jev service is unavailable.", retryable: true } };
274
+ return { ok: false, error: { code: "invalid_response", message: "Jev returned an invalid response." } };
248
275
  }
249
276
 
250
277
  export async function evaluateJev(state: McpExtensionState, value: JevEvaluateInput, options: { purpose: "script" | "semantic-search"; signal?: AbortSignal; budget?: JevBudget; observedSources?: readonly string[] }): Promise<JevEvaluationEnvelope> {
251
278
  let settings: ResolvedJevSettings;
252
279
  let input: JevEvaluateInput;
253
280
  try { settings = options.purpose === "semantic-search" ? resolveSemanticJevSettings(state) : validateJevSettings(state.config.settings?.jev); input = validateJevEvaluateInput(value, settings); }
254
- catch { return { ok: false, error: { code: "invalid_request", message: "Invalid TypeSafe evaluation request or settings." } }; }
255
- if ((options.purpose === "script" && !settings.scriptEvaluation) || (options.purpose === "semantic-search" && !settings.semanticSearch)) return { ok: false, error: { code: "disabled", message: "TypeSafe evaluation is disabled." } };
281
+ catch { return { ok: false, error: { code: "invalid_request", message: "Invalid Jev evaluation request or settings." } }; }
282
+ if ((options.purpose === "script" && !settings.scriptEvaluation) || (options.purpose === "semantic-search" && !settings.semanticSearch)) return { ok: false, error: { code: "disabled", message: "Jev evaluation is disabled." } };
256
283
  const sources = new Set([...(input.sources ?? []), ...(options.observedSources ?? [])]);
257
284
  if (!areJevSourcesAllowed(state, settings, sources)) return { ok: false, error: { code: "data_policy_denied", message: "Evaluation sources are not allowed by policy." } };
258
285
  const bytes = Buffer.byteLength(JSON.stringify(input));
259
- if (options.budget && !options.budget.consume(bytes)) return { ok: false, error: { code: "budget_exhausted", message: "TypeSafe evaluation budget exhausted." } };
286
+ if (options.budget && !options.budget.consume(bytes)) return { ok: false, error: { code: "budget_exhausted", message: "Jev evaluation budget exhausted." } };
260
287
  const resolved = getHost(state, settings);
261
288
  if (!("client" in resolved)) return resolved;
262
289
  const deadline = new AbortController();
263
290
  let timedOut = false;
264
- const timer = setTimeout(() => { timedOut = true; deadline.abort(new Error("TypeSafe evaluation deadline exceeded")); }, settings.requestTimeoutMs);
291
+ const timer = setTimeout(() => { timedOut = true; deadline.abort(new Error("Jev evaluation deadline exceeded")); }, settings.requestTimeoutMs);
265
292
  const signal = combineAbortSignals(options.signal, state.owner.signal, deadline.signal);
266
293
  try {
267
294
  const request = { state: input.state, questions: input.questions, model: settings.model };
268
295
  const raw = await resolved.client.systemOne(request as Parameters<TypeSafeClient["systemOne"]>[0], { ...(signal ? { signal } : {}), timeout: settings.requestTimeoutMs, retry: { maxRetries: settings.maxRetries } });
269
296
  try { return { ok: true, data: validateResponse(raw, input) }; }
270
- catch { return { ok: false, error: { code: "invalid_response", message: "TypeSafe returned an invalid response." } }; }
297
+ catch { return { ok: false, error: { code: "invalid_response", message: "Jev returned an invalid response." } }; }
271
298
  } catch (error) { return failure(error, signal, timedOut); }
272
299
  finally { clearTimeout(timer); }
273
300
  }
@@ -25,8 +25,8 @@ export interface JevEvaluationData {
25
25
 
26
26
  export type JevErrorCode =
27
27
  | "disabled" | "invalid_request" | "data_policy_denied" | "budget_exhausted"
28
- | "credential_missing" | "credential_unavailable" | "authentication_failed"
29
- | "timeout" | "aborted" | "rate_limited" | "service_unavailable" | "invalid_response";
28
+ | "endpoint_unavailable" | "credential_missing" | "credential_unavailable" | "authentication_failed"
29
+ | "payment_required" | "timeout" | "aborted" | "rate_limited" | "service_unavailable" | "invalid_response";
30
30
 
31
31
  export type JevEvaluationEnvelope =
32
32
  | { ok: true; data: JevEvaluationData }
@@ -1,20 +1,44 @@
1
1
  import { createSecureKeyringStore, type SecureKeyringStore } from "./secure-keyring.ts";
2
2
 
3
+ /** The Jev decisions endpoint used when `SYSTEMONE_ENDPOINT` is not set. */
4
+ export const JEV_DEFAULT_ENDPOINT = "https://api.typesafe.ai/v1/systemone";
5
+ const SYSTEMONE_ENDPOINT_ENV = "SYSTEMONE_ENDPOINT";
6
+ const SYSTEMONE_API_KEY_ENV = "SYSTEMONE_API_KEY";
7
+ /** TypeSafe-issued key: honored only for the default endpoint and never sent anywhere else. */
8
+ const LEGACY_TYPESAFE_API_KEY_ENV = "TYPESAFE_API_KEY";
9
+ /** Origin recorded in version 1 credential records. */
3
10
  export const TYPESAFE_API_ORIGIN = "https://api.typesafe.ai";
4
- export const JEV_KEYRING_SERVICE = "pi-mcp-adapter.service-key";
11
+ /** Version 1 credential account, still read for the default endpoint. */
5
12
  export const JEV_KEYRING_ACCOUNT = "typesafe@sha256(https://api.typesafe.ai)";
13
+ export const JEV_KEYRING_SERVICE = "pi-mcp-adapter.service-key";
14
+
15
+ const MAX_ENDPOINT_LENGTH = 512;
16
+ /** The path `@typesafe-ai/sdk` appends to whatever base URL it is given. */
17
+ export const JEV_SDK_PATH = "/v1/systemone";
18
+
19
+ export interface ResolvedJevEndpoint {
20
+ /** Canonical absolute URL sent to the provider, for example `https://opencode.ai/zen/v1/systemone`. */
21
+ readonly href: string;
22
+ /** URL origin, used as the SDK base URL. */
23
+ readonly origin: string;
24
+ /** URL path, rewritten onto the SDK's fixed path by the pinned fetch. */
25
+ readonly path: string;
26
+ }
27
+
28
+ export type JevEndpointResolution =
29
+ | { status: "resolved"; source: "environment" | "default"; endpoint: ResolvedJevEndpoint }
30
+ | { status: "unavailable"; message: string };
6
31
 
7
32
  interface StoredJevKey {
8
- version: 1;
9
- provider: "typesafe";
10
- origin: typeof TYPESAFE_API_ORIGIN;
33
+ version: 2;
34
+ endpoint: string;
11
35
  apiKey: string;
12
36
  }
13
37
 
14
38
  export class JevCredentialStoreError extends Error {
15
39
  readonly code = "JEV_CREDENTIAL_STORE_UNAVAILABLE";
16
40
  constructor(readonly operation: "read" | "write" | "remove", cause: unknown) {
17
- super(`TypeSafe API key secure credential store unavailable during ${operation}. Configure or unlock the OS credential store and retry.`, { cause });
41
+ super(`Jev API key secure credential store unavailable during ${operation}. Configure or unlock the OS credential store and retry.`, { cause });
18
42
  this.name = "JevCredentialStoreError";
19
43
  }
20
44
  }
@@ -26,54 +50,143 @@ export type JevCredentialResolution =
26
50
 
27
51
  function validateApiKey(value: unknown): string {
28
52
  if (typeof value !== "string" || value.trim().length === 0 || /[\u0000-\u001f\u007f]/.test(value)) {
29
- throw new Error("TypeSafe API key must be a non-empty string without control characters");
53
+ throw new Error("Jev API key must be a non-empty string without control characters");
30
54
  }
31
55
  return value;
32
56
  }
33
57
 
58
+ /**
59
+ * Constrains the operator-supplied endpoint to an absolute HTTPS URL without credentials, query, or fragment, so
60
+ * the pinned fetch can match it exactly and a stray value cannot redirect requests or smuggle a query string.
61
+ */
62
+ function parseEndpoint(raw: string, label: string): ResolvedJevEndpoint {
63
+ if (typeof raw !== "string" || raw.trim().length === 0) throw new Error(`${label} must be a non-empty URL`);
64
+ if (raw.length > MAX_ENDPOINT_LENGTH) throw new Error(`${label} must be at most ${MAX_ENDPOINT_LENGTH} characters`);
65
+ if (/[\u0000-\u001f\u007f\s]/.test(raw)) throw new Error(`${label} must not contain whitespace or control characters`);
66
+ let url: URL;
67
+ try { url = new URL(raw); }
68
+ catch { throw new Error(`${label} must be an absolute URL`); }
69
+ if (url.protocol !== "https:") throw new Error(`${label} must use https`);
70
+ if (url.username !== "" || url.password !== "") throw new Error(`${label} must not embed credentials`);
71
+ if (url.search !== "") throw new Error(`${label} must not include a query string`);
72
+ if (url.hash !== "") throw new Error(`${label} must not include a fragment`);
73
+ if (url.pathname === "" || url.pathname === "/") throw new Error(`${label} must include a path such as ${JEV_SDK_PATH}`);
74
+ return { href: `${url.origin}${url.pathname}`, origin: url.origin, path: url.pathname };
75
+ }
76
+
77
+ /**
78
+ * Resolves the Jev endpoint. An invalid `SYSTEMONE_ENDPOINT` resolves to `unavailable` instead of silently falling
79
+ * back to the default: sending an operator's judgment payload to a provider they did not ask for would be a data leak.
80
+ */
81
+ export function resolveJevEndpoint(env: NodeJS.ProcessEnv = process.env): JevEndpointResolution {
82
+ if (Object.hasOwn(env, SYSTEMONE_ENDPOINT_ENV)) {
83
+ try { return { status: "resolved", source: "environment", endpoint: parseEndpoint(env[SYSTEMONE_ENDPOINT_ENV] as string, SYSTEMONE_ENDPOINT_ENV) }; }
84
+ catch (error) { return { status: "unavailable", message: error instanceof Error ? error.message : `${SYSTEMONE_ENDPOINT_ENV} is invalid.` }; }
85
+ }
86
+ return { status: "resolved", source: "default", endpoint: parseEndpoint(JEV_DEFAULT_ENDPOINT, "the default Jev endpoint") };
87
+ }
88
+
89
+ /** Keyring account for an endpoint, so keys for different providers coexist instead of overwriting each other. */
90
+ export function jevKeyringAccount(endpoint: ResolvedJevEndpoint): string {
91
+ return `systemone@sha256(${endpoint.href})`;
92
+ }
93
+
34
94
  function store(): SecureKeyringStore {
35
95
  return createSecureKeyringStore(JEV_KEYRING_SERVICE);
36
96
  }
37
97
 
38
- function readStoredKey(secretStore: SecureKeyringStore = store()): string | undefined {
39
- let payload: string | undefined;
40
- try { payload = secretStore.read(JEV_KEYRING_ACCOUNT); }
41
- catch (error) { throw new JevCredentialStoreError("read", error); }
42
- if (payload === undefined) return undefined;
43
- try {
44
- const value: unknown = JSON.parse(payload);
45
- if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("invalid record");
46
- const record = value as Partial<StoredJevKey>;
47
- const keys = Object.keys(record);
98
+ function requireEndpoint(endpoint: ResolvedJevEndpoint | undefined): ResolvedJevEndpoint {
99
+ if (endpoint) return endpoint;
100
+ const resolution = resolveJevEndpoint();
101
+ if (resolution.status === "unavailable") throw new Error(resolution.message);
102
+ return resolution.endpoint;
103
+ }
104
+
105
+ /**
106
+ * Version 1 records were written before the endpoint was configurable, so they are accepted only for the default
107
+ * endpoint.
108
+ */
109
+ function parseStoredKey(payload: string, endpoint: ResolvedJevEndpoint): string {
110
+ let value: unknown;
111
+ try { value = JSON.parse(payload); }
112
+ catch { throw new Error("invalid record"); }
113
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("invalid record");
114
+ const record = value as Record<string, unknown>;
115
+ const keys = Object.keys(record);
116
+ if (record.version === 2) {
117
+ if (keys.length !== 3 || !["version", "endpoint", "apiKey"].every(key => Object.hasOwn(record, key))) throw new Error("invalid record fields");
118
+ if (record.endpoint !== endpoint.href) throw new Error("invalid or mismatched record");
119
+ return validateApiKey(record.apiKey);
120
+ }
121
+ if (record.version === 1) {
48
122
  if (keys.length !== 4 || !["version", "provider", "origin", "apiKey"].every(key => Object.hasOwn(record, key))) throw new Error("invalid record fields");
49
- if (record.version !== 1 || record.provider !== "typesafe" || record.origin !== TYPESAFE_API_ORIGIN) throw new Error("invalid or mismatched record");
123
+ if (record.provider !== "typesafe" || record.origin !== TYPESAFE_API_ORIGIN) throw new Error("invalid or mismatched record");
124
+ if (endpoint.href !== JEV_DEFAULT_ENDPOINT) throw new Error("invalid or mismatched record");
50
125
  return validateApiKey(record.apiKey);
126
+ }
127
+ throw new Error("invalid record version");
128
+ }
129
+
130
+ function readStoredKey(secretStore: SecureKeyringStore, endpoint: ResolvedJevEndpoint): string | undefined {
131
+ try {
132
+ let payload = secretStore.read(jevKeyringAccount(endpoint));
133
+ if (payload === undefined && endpoint.href === JEV_DEFAULT_ENDPOINT) payload = secretStore.read(JEV_KEYRING_ACCOUNT);
134
+ return payload === undefined ? undefined : parseStoredKey(payload, endpoint);
51
135
  } catch (error) {
52
136
  throw new JevCredentialStoreError("read", error);
53
137
  }
54
138
  }
55
139
 
56
- export function resolveJevCredential(env: NodeJS.ProcessEnv = process.env, secretStore?: SecureKeyringStore): JevCredentialResolution {
57
- if (Object.hasOwn(env, "TYPESAFE_API_KEY")) {
58
- try { return { status: "present", source: "environment", apiKey: validateApiKey(env.TYPESAFE_API_KEY) }; }
59
- catch { return { status: "unavailable", message: "TYPESAFE_API_KEY is present but invalid." }; }
140
+ export function resolveJevCredential(
141
+ env: NodeJS.ProcessEnv = process.env,
142
+ endpoint?: ResolvedJevEndpoint,
143
+ secretStore: SecureKeyringStore = store(),
144
+ ): JevCredentialResolution {
145
+ let target: ResolvedJevEndpoint;
146
+ if (endpoint) target = endpoint;
147
+ else {
148
+ const resolution = resolveJevEndpoint(env);
149
+ if (resolution.status === "unavailable") return resolution;
150
+ target = resolution.endpoint;
60
151
  }
61
- try {
62
- const apiKey = readStoredKey(secretStore ?? store());
63
- return apiKey === undefined ? { status: "missing" } : { status: "present", source: "keyring", apiKey };
64
- } catch (error) {
152
+ if (Object.hasOwn(env, SYSTEMONE_API_KEY_ENV)) {
153
+ try { return { status: "present", source: "environment", apiKey: validateApiKey(env[SYSTEMONE_API_KEY_ENV]) }; }
154
+ catch { return { status: "unavailable", message: `${SYSTEMONE_API_KEY_ENV} is present but invalid.` }; }
155
+ }
156
+ const legacySet = Object.hasOwn(env, LEGACY_TYPESAFE_API_KEY_ENV);
157
+ if (legacySet && target.href === JEV_DEFAULT_ENDPOINT) {
158
+ try { return { status: "present", source: "environment", apiKey: validateApiKey(env[LEGACY_TYPESAFE_API_KEY_ENV]) }; }
159
+ catch { return { status: "unavailable", message: `${LEGACY_TYPESAFE_API_KEY_ENV} is present but invalid.` }; }
160
+ }
161
+ let stored: string | undefined;
162
+ try { stored = readStoredKey(secretStore, target); }
163
+ catch (error) {
65
164
  if (!(error instanceof JevCredentialStoreError)) throw error;
66
165
  return { status: "unavailable", message: error.message };
67
166
  }
167
+ if (stored !== undefined) return { status: "present", source: "keyring", apiKey: stored };
168
+ // A TypeSafe-issued credential is never sent to another endpoint, but it must not mask a credential stored for
169
+ // this endpoint either, so it is only reported once nothing for this endpoint resolves.
170
+ if (legacySet) {
171
+ return {
172
+ status: "unavailable",
173
+ message: `${LEGACY_TYPESAFE_API_KEY_ENV} is a TypeSafe credential and is not sent to ${target.href}; set ${SYSTEMONE_API_KEY_ENV} for that endpoint.`,
174
+ };
175
+ }
176
+ return { status: "missing" };
68
177
  }
69
178
 
70
- export function saveJevApiKey(apiKey: string, secretStore: SecureKeyringStore = store()): void {
71
- const record: StoredJevKey = { version: 1, provider: "typesafe", origin: TYPESAFE_API_ORIGIN, apiKey: validateApiKey(apiKey) };
72
- try { secretStore.write(JEV_KEYRING_ACCOUNT, JSON.stringify(record)); }
179
+ export function saveJevApiKey(apiKey: string, endpoint?: ResolvedJevEndpoint, secretStore: SecureKeyringStore = store()): void {
180
+ const target = requireEndpoint(endpoint);
181
+ const record: StoredJevKey = { version: 2, endpoint: target.href, apiKey: validateApiKey(apiKey) };
182
+ try { secretStore.write(jevKeyringAccount(target), JSON.stringify(record)); }
73
183
  catch (error) { throw new JevCredentialStoreError("write", error); }
74
184
  }
75
185
 
76
- export function removeJevApiKey(secretStore: SecureKeyringStore = store()): void {
77
- try { secretStore.remove(JEV_KEYRING_ACCOUNT); }
78
- catch (error) { throw new JevCredentialStoreError("remove", error); }
186
+ export function removeJevApiKey(endpoint?: ResolvedJevEndpoint, secretStore: SecureKeyringStore = store()): void {
187
+ const target = requireEndpoint(endpoint);
188
+ try {
189
+ secretStore.remove(jevKeyringAccount(target));
190
+ if (target.href === JEV_DEFAULT_ENDPOINT) secretStore.remove(JEV_KEYRING_ACCOUNT);
191
+ } catch (error) { throw new JevCredentialStoreError("remove", error); }
79
192
  }
@@ -1191,9 +1191,8 @@ export async function getValidToken(
1191
1191
  }
1192
1192
  }
1193
1193
 
1194
- // No expiration info or no refresh token, assume valid
1195
1194
  authority()
1196
- return entry.tokens
1195
+ return null
1197
1196
  }
1198
1197
 
1199
1198
  /**
@@ -213,7 +213,7 @@ export async function runMcpScript(
213
213
  let evaluationAttempts = 0;
214
214
  let evaluationBytes = 0;
215
215
  let evaluationTokensRemaining = jevSettings.maxEvaluationTokensPerScript;
216
- const tokenBudgetExhausted = (): JevEvaluationEnvelope => ({ ok: false, error: { code: "budget_exhausted", message: "TypeSafe evaluation token budget exhausted." } });
216
+ const tokenBudgetExhausted = (): JevEvaluationEnvelope => ({ ok: false, error: { code: "budget_exhausted", message: "Jev evaluation token budget exhausted." } });
217
217
  const chargeEvaluationTokens = (envelope: JevEvaluationEnvelope): JevEvaluationEnvelope => {
218
218
  if (!envelope.ok) return envelope;
219
219
  const used = envelope.data.usage.inputTokens + envelope.data.usage.outputTokens;
@@ -226,16 +226,16 @@ export async function runMcpScript(
226
226
  };
227
227
  const admitEvaluation = (input: unknown): JevEvaluationEnvelope | undefined => {
228
228
  if (++evaluationAttempts > jevSettings.maxEvaluationsPerScript) {
229
- return { ok: false, error: { code: "budget_exhausted", message: "TypeSafe evaluation count budget exhausted." } };
229
+ return { ok: false, error: { code: "budget_exhausted", message: "Jev evaluation count budget exhausted." } };
230
230
  }
231
231
  if (evaluationTokensRemaining === 0) return tokenBudgetExhausted();
232
232
  let serialized: string | undefined;
233
233
  try { serialized = JSON.stringify(input); }
234
- catch { return { ok: false, error: { code: "invalid_request", message: "Invalid TypeSafe evaluation request." } }; }
235
- if (serialized === undefined) return { ok: false, error: { code: "invalid_request", message: "Invalid TypeSafe evaluation request." } };
234
+ catch { return { ok: false, error: { code: "invalid_request", message: "Invalid Jev evaluation request." } }; }
235
+ if (serialized === undefined) return { ok: false, error: { code: "invalid_request", message: "Invalid Jev evaluation request." } };
236
236
  const bytes = Buffer.byteLength(serialized, "utf8");
237
237
  if (bytes > jevSettings.maxEvaluationBytesPerScript - evaluationBytes) {
238
- return { ok: false, error: { code: "budget_exhausted", message: "TypeSafe evaluation byte budget exhausted." } };
238
+ return { ok: false, error: { code: "budget_exhausted", message: "Jev evaluation byte budget exhausted." } };
239
239
  }
240
240
  evaluationBytes += bytes;
241
241
  return undefined;
@@ -257,7 +257,7 @@ export async function runMcpScript(
257
257
  envelope = chargeEvaluationTokens(envelope);
258
258
  throwIfAborted(callSignal);
259
259
  if (!reserveIntermediateBytes(JSON.stringify(envelope))) {
260
- envelope = { ok: false, error: { code: "budget_exhausted", message: "TypeSafe evaluation exceeds the remaining mcpScript intermediate transfer budget (16 MiB per script)." } };
260
+ envelope = { ok: false, error: { code: "budget_exhausted", message: "Jev evaluation exceeds the remaining mcpScript intermediate transfer budget (16 MiB per script)." } };
261
261
  }
262
262
  calls[index] = envelope.ok
263
263
  ? {