@opencow-ai/opencow-agent-sdk 0.4.25 → 0.4.26

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.
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Codex 插件把第三方授权写成「按名引用」的宿主专属字段:`env_vars`、
3
+ * `bearer_token_env_var`、`env_http_headers`、`http_headers`。MCP 协议与本运行时
4
+ * 都不读这些键,`McpServerConfigSchema` 会把它们当未知字段静默丢弃 —— 结果是插件
5
+ * MCP 启动时没有 Authorization / env,请求必然失败。
6
+ *
7
+ * 因此在校验前把它们翻译成运行时真正读取的 `env` / `headers`,值写成 `${VAR}`
8
+ * 引用,交由 `resolvePluginMcpEnvironment` 展开(值来源见 `SdkManagedPlugin.env`)。
9
+ *
10
+ * 与 Host 侧 `src/shared/mcpConfigConvert.ts` 同一语义;Host 的渲染层依赖使它无法
11
+ * 引用 SDK,两份实现必须同步修改。差异:Host 还要把 mcpb/Claude 的占位写法
12
+ * (`${input:…}`、`${user_config.…}`、`${{ secrets.… }}`)改写成待配置变量,
13
+ * 那是无 user-config 通道时的兜底;这里 `${user_config.…}` 由运行时原生支持,
14
+ * 不做改写。
15
+ */
16
+ /**
17
+ * 翻译单个 MCP server 配置(不是 `{ mcpServers: … }` 包装层)。
18
+ * 输入不被修改;非对象输入原样返回。
19
+ */
20
+ export declare function normalizeCodexMcpFields(input: unknown): Record<string, unknown>;
@@ -4,9 +4,12 @@
4
4
  /**
5
5
  * Expand environment variables in a string value
6
6
  * Handles ${VAR} and ${VAR:-default} syntax
7
+ * @param env Lookup source for variable values. Defaults to `process.env`;
8
+ * callers pass a per-plugin overlay so plugin-scoped values (e.g. credentials
9
+ * the host collected for that plugin) win over the ambient process env.
7
10
  * @returns Object with expanded string and list of missing variables
8
11
  */
9
- export declare function expandEnvVarsInString(value: string): {
12
+ export declare function expandEnvVarsInString(value: string, env?: Record<string, string | undefined>): {
10
13
  expanded: string;
11
14
  missingVars: string[];
12
15
  };
@@ -21,6 +21,7 @@ export declare function resolvePluginLspEnvironment(config: LspServerConfig, plu
21
21
  path: string;
22
22
  source: string;
23
23
  dataPath?: string;
24
+ env?: Record<string, string>;
24
25
  }, userConfig?: PluginOptionValues, _errors?: PluginError[]): LspServerConfig;
25
26
  /**
26
27
  * Add plugin scope to LSP server configs
@@ -48,6 +48,7 @@ export declare function resolvePluginMcpEnvironment(config: McpServerConfig, plu
48
48
  path: string;
49
49
  source: string;
50
50
  dataPath?: string;
51
+ env?: Record<string, string>;
51
52
  }, userConfig?: UserConfigValues, errors?: PluginError[], pluginName?: string, serverName?: string): McpServerConfig;
52
53
  /**
53
54
  * Get MCP servers from a specific plugin with environment variable resolution and scoping
package/dist/cli.mjs CHANGED
@@ -974,6 +974,11 @@ var init_paths = __esm(() => {
974
974
  });
975
975
 
976
976
  // src/session/layout/LayoutProfile.ts
977
+ var exports_LayoutProfile = {};
978
+ __export(exports_LayoutProfile, {
979
+ DEFAULT_LAYOUT_PROFILE: () => DEFAULT_LAYOUT_PROFILE,
980
+ resolveLayoutProfile: () => resolveLayoutProfile
981
+ });
977
982
  function resolveBinding(binding) {
978
983
  if (typeof binding === "string") {
979
984
  return BUILT_IN_BINDINGS[binding];
@@ -47761,6 +47766,63 @@ var init_toolSchemaCache = __esm(() => {
47761
47766
  });
47762
47767
 
47763
47768
  // src/providers/shared/auth.ts
47769
+ var exports_auth = {};
47770
+ __export(exports_auth, {
47771
+ calculateApiKeyHelperTTL: () => calculateApiKeyHelperTTL,
47772
+ checkAndRefreshOAuthTokenIfNeeded: () => checkAndRefreshOAuthTokenIfNeeded,
47773
+ checkGcpCredentialsValid: () => checkGcpCredentialsValid,
47774
+ clearApiKeyHelperCache: () => clearApiKeyHelperCache,
47775
+ clearAwsCredentialsCache: () => clearAwsCredentialsCache,
47776
+ clearGcpCredentialsCache: () => clearGcpCredentialsCache,
47777
+ clearOAuthTokenCache: () => clearOAuthTokenCache,
47778
+ getAccountInformation: () => getAccountInformation,
47779
+ getAnthropicApiKey: () => getAnthropicApiKey,
47780
+ getAnthropicApiKeyWithSource: () => getAnthropicApiKeyWithSource,
47781
+ getApiKeyFromApiKeyHelper: () => getApiKeyFromApiKeyHelper,
47782
+ getApiKeyFromApiKeyHelperCached: () => getApiKeyFromApiKeyHelperCached,
47783
+ getApiKeyFromConfigOrMacOSKeychain: () => getApiKeyFromConfigOrMacOSKeychain,
47784
+ getApiKeyHelperElapsedMs: () => getApiKeyHelperElapsedMs,
47785
+ getAuthTokenSource: () => getAuthTokenSource,
47786
+ getClaudeAIOAuthTokens: () => getClaudeAIOAuthTokens,
47787
+ getClaudeAIOAuthTokensAsync: () => getClaudeAIOAuthTokensAsync,
47788
+ getConfiguredApiKeyHelper: () => getConfiguredApiKeyHelper,
47789
+ getOauthAccountInfo: () => getOauthAccountInfo,
47790
+ getOtelHeadersFromHelper: () => getOtelHeadersFromHelper,
47791
+ getRateLimitTier: () => getRateLimitTier,
47792
+ getSubscriptionName: () => getSubscriptionName,
47793
+ getSubscriptionType: () => getSubscriptionType,
47794
+ handleOAuth401Error: () => handleOAuth401Error,
47795
+ hasAnthropicApiKeyAuth: () => hasAnthropicApiKeyAuth,
47796
+ hasOpusAccess: () => hasOpusAccess,
47797
+ hasProfileScope: () => hasProfileScope,
47798
+ is1PApiCustomer: () => is1PApiCustomer,
47799
+ isAnthropicAuthEnabled: () => isAnthropicAuthEnabled,
47800
+ isAwsAuthRefreshFromProjectSettings: () => isAwsAuthRefreshFromProjectSettings,
47801
+ isAwsCredentialExportFromProjectSettings: () => isAwsCredentialExportFromProjectSettings,
47802
+ isClaudeAISubscriber: () => isClaudeAISubscriber,
47803
+ isConsumerSubscriber: () => isConsumerSubscriber,
47804
+ isCustomApiKeyApproved: () => isCustomApiKeyApproved,
47805
+ isEnterpriseSubscriber: () => isEnterpriseSubscriber,
47806
+ isGcpAuthRefreshFromProjectSettings: () => isGcpAuthRefreshFromProjectSettings,
47807
+ isMaxSubscriber: () => isMaxSubscriber,
47808
+ isOtelHeadersHelperFromProjectOrLocalSettings: () => isOtelHeadersHelperFromProjectOrLocalSettings,
47809
+ isOverageProvisioningAllowed: () => isOverageProvisioningAllowed,
47810
+ isProSubscriber: () => isProSubscriber,
47811
+ isTeamPremiumSubscriber: () => isTeamPremiumSubscriber,
47812
+ isTeamSubscriber: () => isTeamSubscriber,
47813
+ isUsing3PServices: () => isUsing3PServices,
47814
+ prefetchApiKeyFromApiKeyHelperIfSafe: () => prefetchApiKeyFromApiKeyHelperIfSafe,
47815
+ prefetchAwsCredentialsAndBedRockInfoIfSafe: () => prefetchAwsCredentialsAndBedRockInfoIfSafe,
47816
+ prefetchGcpCredentialsIfSafe: () => prefetchGcpCredentialsIfSafe,
47817
+ refreshAndGetAwsCredentials: () => refreshAndGetAwsCredentials,
47818
+ refreshAwsAuth: () => refreshAwsAuth,
47819
+ refreshGcpAuth: () => refreshGcpAuth,
47820
+ refreshGcpCredentialsIfNeeded: () => refreshGcpCredentialsIfNeeded,
47821
+ removeApiKey: () => removeApiKey,
47822
+ saveApiKey: () => saveApiKey,
47823
+ saveOAuthTokensIfNeeded: () => saveOAuthTokensIfNeeded,
47824
+ validateForceLoginOrg: () => validateForceLoginOrg
47825
+ });
47764
47826
  import { exec } from "child_process";
47765
47827
  import { join as join18 } from "path";
47766
47828
  function isManagedOAuthContext() {
@@ -48350,6 +48412,11 @@ async function saveApiKey(apiKey) {
48350
48412
  getApiKeyFromConfigOrMacOSKeychain.cache.clear?.();
48351
48413
  clearLegacyApiKeyPrefetch();
48352
48414
  }
48415
+ function isCustomApiKeyApproved(apiKey) {
48416
+ const config = getGlobalConfig();
48417
+ const normalizedKey = normalizeApiKeyForConfig(apiKey);
48418
+ return config.customApiKeyResponses?.approved?.includes(normalizedKey) ?? false;
48419
+ }
48353
48420
  async function removeApiKey() {
48354
48421
  await maybeRemoveApiKeyFromMacOSKeychain();
48355
48422
  saveGlobalConfig((current) => ({
@@ -48588,6 +48655,10 @@ function isOverageProvisioningAllowed() {
48588
48655
  }
48589
48656
  return true;
48590
48657
  }
48658
+ function hasOpusAccess() {
48659
+ const subscriptionType = getSubscriptionType();
48660
+ return subscriptionType === "max" || subscriptionType === "enterprise" || subscriptionType === "team" || subscriptionType === "pro" || subscriptionType === null;
48661
+ }
48591
48662
  function getSubscriptionType() {
48592
48663
  if (shouldUseMockSubscription()) {
48593
48664
  return getMockSubscriptionType();
@@ -80953,7 +81024,7 @@ function buildQueryContextFromSdk(options) {
80953
81024
  const memoryBaseDir = resolveEnvVar("REMOTE_MEMORY_DIR") ?? configHomeDir;
80954
81025
  const autoMemEnvOverride = resolveEnvVar("MEMORY_PATH_OVERRIDE");
80955
81026
  const autoMemSettingsOverride = resolvedFileSystemLayer === "disabled" ? undefined : readAutoMemSettingsOverride();
80956
- init_managedPath();
81027
+ const { getManagedFilePath } = (init_managedPath(), __toCommonJS(exports_managedPath));
80957
81028
  const paths = computeSessionPaths({
80958
81029
  cwd,
80959
81030
  projectRoot: cwd,
@@ -83882,6 +83953,11 @@ var init_platform2 = __esm(() => {
83882
83953
  });
83883
83954
 
83884
83955
  // src/session/settings/managedPath.ts
83956
+ var exports_managedPath = {};
83957
+ __export(exports_managedPath, {
83958
+ getManagedFilePath: () => getManagedFilePath,
83959
+ getManagedSettingsDropInDir: () => getManagedSettingsDropInDir
83960
+ });
83885
83961
  import { join as join22 } from "path";
83886
83962
  var getManagedFilePath, getManagedSettingsDropInDir;
83887
83963
  var init_managedPath = __esm(() => {
@@ -84078,8 +84154,8 @@ function createBootQueryContext() {
84078
84154
  }
84079
84155
  const sessionId = randomUUID();
84080
84156
  init_paths();
84081
- init_LayoutProfile();
84082
- init_managedPath();
84157
+ const { DEFAULT_LAYOUT_PROFILE } = (init_LayoutProfile(), __toCommonJS(exports_LayoutProfile));
84158
+ const { getManagedFilePath } = (init_managedPath(), __toCommonJS(exports_managedPath));
84083
84159
  const { homedir } = __require("node:os");
84084
84160
  const bootConfigHomeDir = process.env.CLAUDE_CONFIG_DIR ? process.env.CLAUDE_CONFIG_DIR.normalize("NFC") : (homedir() + "/.opencow").normalize("NFC");
84085
84161
  const bootMemoryBaseDir = resolveEnvVar("REMOTE_MEMORY_DIR") ?? bootConfigHomeDir;
@@ -84555,6 +84631,7 @@ var init_envVars = __esm(() => {
84555
84631
  DISABLE_EARLY_INPUT: envVar("OPENCOW_DISABLE_EARLY_INPUT"),
84556
84632
  ENABLE_EXTENDED_KEYS: envVar("OPENCOW_ENABLE_EXTENDED_KEYS"),
84557
84633
  ENABLE_STREAM_WATCHDOG: envVar("OPENCOW_ENABLE_STREAM_WATCHDOG"),
84634
+ SKILL_LISTING: envVar("OPENCOW_SKILL_LISTING"),
84558
84635
  STREAM_IDLE_TIMEOUT_MS: envVar("OPENCOW_STREAM_IDLE_TIMEOUT_MS"),
84559
84636
  UNBOUNDED_CONNECTION_RETRY: envVar("OPENCOW_UNBOUNDED_CONNECTION_RETRY"),
84560
84637
  STREAM_REPLAY_RETRIES: envVar("OPENCOW_STREAM_REPLAY_RETRIES"),
@@ -86601,7 +86678,7 @@ function printStartupScreen() {
86601
86678
  const sLen = ` ● ${sL} Ready — type /help to begin`.length;
86602
86679
  out.push(boxRow(sRow, W, sLen));
86603
86680
  out.push(`${rgb(...BORDER)}╚${"═".repeat(W - 2)}╝${RESET}`);
86604
- out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.25"}${RESET}`);
86681
+ out.push(` ${DIM}${rgb(...DIMCOL)}opencow ${RESET}${rgb(...ACCENT)}v${"0.4.26"}${RESET}`);
86605
86682
  out.push("");
86606
86683
  process.stdout.write(out.join(`
86607
86684
  `) + `
@@ -131694,11 +131771,11 @@ function getPluginErrorMessage(error) {
131694
131771
  }
131695
131772
 
131696
131773
  // src/capabilities/mcp/envExpansion.ts
131697
- function expandEnvVarsInString(value) {
131774
+ function expandEnvVarsInString(value, env = process.env) {
131698
131775
  const missingVars = [];
131699
131776
  const expanded = value.replace(/\$\{([^}]+)\}/g, (match, varContent) => {
131700
131777
  const [varName, defaultValue] = varContent.split(":-", 2);
131701
- const envValue = process.env[varName];
131778
+ const envValue = env[varName];
131702
131779
  if (envValue !== undefined) {
131703
131780
  return envValue;
131704
131781
  }
@@ -131714,6 +131791,61 @@ function expandEnvVarsInString(value) {
131714
131791
  };
131715
131792
  }
131716
131793
 
131794
+ // src/capabilities/mcp/codexConfigFields.ts
131795
+ function isPlainObject6(value) {
131796
+ return !!value && typeof value === "object" && !Array.isArray(value);
131797
+ }
131798
+ function setMissing(record, key, value) {
131799
+ if (record[key] !== undefined)
131800
+ return;
131801
+ record[key] = value;
131802
+ }
131803
+ function recordFor(config, key) {
131804
+ const existing = config[key];
131805
+ if (isPlainObject6(existing))
131806
+ return existing;
131807
+ const created = {};
131808
+ config[key] = created;
131809
+ return created;
131810
+ }
131811
+ function stringRecordEntries(value) {
131812
+ if (!isPlainObject6(value))
131813
+ return [];
131814
+ return Object.entries(value).filter((entry) => typeof entry[1] === "string");
131815
+ }
131816
+ function normalizeCodexMcpFields(input) {
131817
+ if (!isPlainObject6(input))
131818
+ return {};
131819
+ const config = structuredClone(input);
131820
+ const envVars = config["env_vars"];
131821
+ if (Array.isArray(envVars)) {
131822
+ for (const name of envVars.filter((item) => typeof item === "string")) {
131823
+ setMissing(recordFor(config, "env"), name, `\${${name}}`);
131824
+ }
131825
+ delete config["env_vars"];
131826
+ }
131827
+ const envHttpHeaders = config["env_http_headers"];
131828
+ if (isPlainObject6(envHttpHeaders)) {
131829
+ for (const [header, varName] of stringRecordEntries(envHttpHeaders)) {
131830
+ setMissing(recordFor(config, "headers"), header, `\${${varName}}`);
131831
+ }
131832
+ delete config["env_http_headers"];
131833
+ }
131834
+ const bearer = config["bearer_token_env_var"];
131835
+ if (typeof bearer === "string" && bearer.trim()) {
131836
+ setMissing(recordFor(config, "headers"), "Authorization", `Bearer \${${bearer.trim()}}`);
131837
+ delete config["bearer_token_env_var"];
131838
+ }
131839
+ const httpHeaders = config["http_headers"];
131840
+ if (isPlainObject6(httpHeaders)) {
131841
+ for (const [header, value] of stringRecordEntries(httpHeaders)) {
131842
+ setMissing(recordFor(config, "headers"), header, value);
131843
+ }
131844
+ delete config["http_headers"];
131845
+ }
131846
+ return config;
131847
+ }
131848
+
131717
131849
  // native-stub:@anthropic-ai/mcpb
131718
131850
  var exports_mcpb = {};
131719
131851
  __export(exports_mcpb, {
@@ -135298,7 +135430,7 @@ async function loadMcpServersFromFile(pluginPath, relativePath, errors, plugin,
135298
135430
  const mcpServers = parsed.mcpServers || parsed;
135299
135431
  const validatedServers = {};
135300
135432
  for (const [name, config] of Object.entries(mcpServers)) {
135301
- const result = McpServerConfigSchema().safeParse(config);
135433
+ const result = McpServerConfigSchema().safeParse(normalizeCodexMcpFields(config));
135302
135434
  if (result.success) {
135303
135435
  validatedServers[name] = result.data;
135304
135436
  } else {
@@ -135379,12 +135511,13 @@ function buildMcpUserConfig(plugin, serverName) {
135379
135511
  }
135380
135512
  function resolvePluginMcpEnvironment(config, plugin, userConfig, errors, pluginName, serverName) {
135381
135513
  const allMissingVars = [];
135514
+ const envLookup = plugin.env ? { ...process.env, ...plugin.env } : process.env;
135382
135515
  const resolveValue = (value) => {
135383
135516
  let resolved = substitutePluginVariables(value, plugin);
135384
135517
  if (userConfig) {
135385
135518
  resolved = substituteUserConfigVariables(resolved, userConfig);
135386
135519
  }
135387
- const { expanded, missingVars } = expandEnvVarsInString(resolved);
135520
+ const { expanded, missingVars } = expandEnvVarsInString(resolved, envLookup);
135388
135521
  allMissingVars.push(...missingVars);
135389
135522
  return expanded;
135390
135523
  };
@@ -235127,7 +235260,7 @@ function getAnthropicEnvMetadata() {
235127
235260
  function getBuildAgeMinutes() {
235128
235261
  if (false)
235129
235262
  ;
235130
- const buildTime = new Date("2026-10-09T14:01:34.561Z").getTime();
235263
+ const buildTime = new Date("2026-10-19T06:49:44.552Z").getTime();
235131
235264
  if (isNaN(buildTime))
235132
235265
  return;
235133
235266
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -256556,12 +256689,13 @@ async function loadLspServersFromManifest(declaration, pluginPath, pluginName, e
256556
256689
  }
256557
256690
  function resolvePluginLspEnvironment(config, plugin, userConfig, _errors) {
256558
256691
  const allMissingVars = [];
256692
+ const envLookup = plugin.env ? { ...process.env, ...plugin.env } : process.env;
256559
256693
  const resolveValue = (value) => {
256560
256694
  let resolved = substitutePluginVariables(value, plugin);
256561
256695
  if (userConfig) {
256562
256696
  resolved = substituteUserConfigVariables(resolved, userConfig);
256563
256697
  }
256564
- const { expanded, missingVars } = expandEnvVarsInString(resolved);
256698
+ const { expanded, missingVars } = expandEnvVarsInString(resolved, envLookup);
256565
256699
  allMissingVars.push(...missingVars);
256566
256700
  return expanded;
256567
256701
  };
@@ -290124,7 +290258,7 @@ async function getAttachments(input, toolUseContext, ideSelection, queuedCommand
290124
290258
  maybe("changed_files", () => getChangedFiles(context)),
290125
290259
  maybe("nested_memory", () => getNestedMemoryAttachments(context)),
290126
290260
  maybe("dynamic_skill", () => getDynamicSkillAttachments(context)),
290127
- maybe("skill_listing", () => getSkillListingAttachments(context)),
290261
+ ...isSkillListingEnabled() ? [maybe("skill_listing", () => getSkillListingAttachments(context))] : [],
290128
290262
  maybe("plan_mode", () => getPlanModeAttachments(messages, toolUseContext)),
290129
290263
  maybe("plan_mode_exit", () => getPlanModeExitAttachment(toolUseContext)),
290130
290264
  ...[],
@@ -290961,6 +291095,9 @@ function resetSentSkillNames() {
290961
291095
  function suppressNextSkillListing() {
290962
291096
  getQueryContext().registries.skillListingSuppressNext = true;
290963
291097
  }
291098
+ function isSkillListingEnabled() {
291099
+ return !isEnvDefinedFalsy(resolveEnvVar("SKILL_LISTING"));
291100
+ }
290964
291101
  async function getSkillListingAttachments(toolUseContext) {
290965
291102
  if (false) {}
290966
291103
  if (!toolUseContext.options.tools.some((t) => toolMatchesName2(t, SKILL_TOOL_NAME))) {
@@ -389512,7 +389649,7 @@ async function enrollTrustedDevice() {
389512
389649
  logForDebugging2("[trusted-device] CLAUDE_TRUSTED_DEVICE_TOKEN env var is set, skipping enrollment (env var takes precedence)");
389513
389650
  return;
389514
389651
  }
389515
- init_auth();
389652
+ const { getClaudeAIOAuthTokens } = (init_auth(), __toCommonJS(exports_auth));
389516
389653
  const accessToken = getClaudeAIOAuthTokens()?.accessToken;
389517
389654
  if (!accessToken) {
389518
389655
  logForDebugging2("[trusted-device] No OAuth token, skipping enrollment");
@@ -480080,7 +480217,7 @@ function buildPrimarySection() {
480080
480217
  }, undefined, false, undefined, this);
480081
480218
  return [{
480082
480219
  label: "Version",
480083
- value: "0.4.25"
480220
+ value: "0.4.26"
480084
480221
  }, {
480085
480222
  label: "Session name",
480086
480223
  value: nameValue
@@ -528159,7 +528296,7 @@ var init_bridge_kick = __esm(() => {
528159
528296
  var call58 = async () => {
528160
528297
  return {
528161
528298
  type: "text",
528162
- value: `${"99.0.0"} (built ${"2026-10-09T14:01:34.561Z"})`
528299
+ value: `${"99.0.0"} (built ${"2026-10-19T06:49:44.552Z"})`
528163
528300
  };
528164
528301
  }, version2, version_default;
528165
528302
  var init_version = __esm(() => {
@@ -545838,7 +545975,7 @@ function WelcomeV2() {
545838
545975
  dimColor: true,
545839
545976
  children: [
545840
545977
  "v",
545841
- "0.4.25",
545978
+ "0.4.26",
545842
545979
  " "
545843
545980
  ]
545844
545981
  }, undefined, true, undefined, this)
@@ -546038,7 +546175,7 @@ function WelcomeV2() {
546038
546175
  dimColor: true,
546039
546176
  children: [
546040
546177
  "v",
546041
- "0.4.25",
546178
+ "0.4.26",
546042
546179
  " "
546043
546180
  ]
546044
546181
  }, undefined, true, undefined, this)
@@ -546264,7 +546401,7 @@ function AppleTerminalWelcomeV2(t0) {
546264
546401
  dimColor: true,
546265
546402
  children: [
546266
546403
  "v",
546267
- "0.4.25",
546404
+ "0.4.26",
546268
546405
  " "
546269
546406
  ]
546270
546407
  }, undefined, true, undefined, this);
@@ -546518,7 +546655,7 @@ function AppleTerminalWelcomeV2(t0) {
546518
546655
  dimColor: true,
546519
546656
  children: [
546520
546657
  "v",
546521
- "0.4.25",
546658
+ "0.4.26",
546522
546659
  " "
546523
546660
  ]
546524
546661
  }, undefined, true, undefined, this);
@@ -567277,7 +567414,7 @@ Usage: claude --remote "your task description"`, () => gracefulShutdown(1));
567277
567414
  pendingHookMessages
567278
567415
  }, renderAndRun);
567279
567416
  }
567280
- }).version("0.4.25 (OpenCow)", "-v, --version", "Output the version number");
567417
+ }).version("0.4.26 (OpenCow)", "-v, --version", "Output the version number");
567281
567418
  program.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
567282
567419
  program.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.");
567283
567420
  if (canUserConfigureAdvisor()) {
@@ -567872,7 +568009,7 @@ if (false) {}
567872
568009
  async function main2() {
567873
568010
  const args = process.argv.slice(2);
567874
568011
  if (args.length === 1 && (args[0] === "--version" || args[0] === "-v" || args[0] === "-V")) {
567875
- console.log(`${"0.4.25"} (OpenCow)`);
568012
+ console.log(`${"0.4.26"} (OpenCow)`);
567876
568013
  return;
567877
568014
  }
567878
568015
  if (args.includes("--provider")) {
@@ -567972,4 +568109,4 @@ async function main2() {
567972
568109
  }
567973
568110
  main2();
567974
568111
 
567975
- //# debugId=C78360B14966554964756E2164756E21
568112
+ //# debugId=D1987FE5FC94AB4064756E2164756E21
package/dist/client.js CHANGED
@@ -19269,6 +19269,7 @@ var init_envVars = __esm(() => {
19269
19269
  DISABLE_EARLY_INPUT: envVar("OPENCOW_DISABLE_EARLY_INPUT"),
19270
19270
  ENABLE_EXTENDED_KEYS: envVar("OPENCOW_ENABLE_EXTENDED_KEYS"),
19271
19271
  ENABLE_STREAM_WATCHDOG: envVar("OPENCOW_ENABLE_STREAM_WATCHDOG"),
19272
+ SKILL_LISTING: envVar("OPENCOW_SKILL_LISTING"),
19272
19273
  STREAM_IDLE_TIMEOUT_MS: envVar("OPENCOW_STREAM_IDLE_TIMEOUT_MS"),
19273
19274
  UNBOUNDED_CONNECTION_RETRY: envVar("OPENCOW_UNBOUNDED_CONNECTION_RETRY"),
19274
19275
  STREAM_REPLAY_RETRIES: envVar("OPENCOW_STREAM_REPLAY_RETRIES"),
@@ -19543,6 +19544,12 @@ var init_paths = __esm(() => {
19543
19544
  });
19544
19545
 
19545
19546
  // src/session/layout/LayoutProfile.ts
19547
+ var exports_LayoutProfile = {};
19548
+ __export(exports_LayoutProfile, {
19549
+ DEFAULT_LAYOUT_PROFILE: () => DEFAULT_LAYOUT_PROFILE,
19550
+ resolveBinding: () => resolveBinding,
19551
+ resolveLayoutProfile: () => resolveLayoutProfile
19552
+ });
19546
19553
  function resolveBinding(binding) {
19547
19554
  if (typeof binding === "string") {
19548
19555
  return BUILT_IN_BINDINGS[binding];
@@ -27244,6 +27251,11 @@ var init_platform = __esm(() => {
27244
27251
  });
27245
27252
 
27246
27253
  // src/session/settings/managedPath.ts
27254
+ var exports_managedPath = {};
27255
+ __export(exports_managedPath, {
27256
+ getManagedFilePath: () => getManagedFilePath,
27257
+ getManagedSettingsDropInDir: () => getManagedSettingsDropInDir
27258
+ });
27247
27259
  import { join as join5 } from "path";
27248
27260
  var getManagedFilePath, getManagedSettingsDropInDir;
27249
27261
  var init_managedPath = __esm(() => {
@@ -90142,8 +90154,8 @@ function createBootQueryContext() {
90142
90154
  }
90143
90155
  const sessionId = randomUUID();
90144
90156
  init_paths();
90145
- init_LayoutProfile();
90146
- init_managedPath();
90157
+ const { DEFAULT_LAYOUT_PROFILE } = (init_LayoutProfile(), __toCommonJS(exports_LayoutProfile));
90158
+ const { getManagedFilePath } = (init_managedPath(), __toCommonJS(exports_managedPath));
90147
90159
  const { homedir } = __require("node:os");
90148
90160
  const bootConfigHomeDir = process.env.CLAUDE_CONFIG_DIR ? process.env.CLAUDE_CONFIG_DIR.normalize("NFC") : (homedir() + "/.opencow").normalize("NFC");
90149
90161
  const bootMemoryBaseDir = resolveEnvVar("REMOTE_MEMORY_DIR") ?? bootConfigHomeDir;
@@ -90491,7 +90503,7 @@ function buildQueryContextFromSdk(options) {
90491
90503
  const memoryBaseDir = resolveEnvVar("REMOTE_MEMORY_DIR") ?? configHomeDir;
90492
90504
  const autoMemEnvOverride = resolveEnvVar("MEMORY_PATH_OVERRIDE");
90493
90505
  const autoMemSettingsOverride = resolvedFileSystemLayer === "disabled" ? undefined : readAutoMemSettingsOverride();
90494
- init_managedPath();
90506
+ const { getManagedFilePath } = (init_managedPath(), __toCommonJS(exports_managedPath));
90495
90507
  const paths = computeSessionPaths({
90496
90508
  cwd,
90497
90509
  projectRoot: cwd,
@@ -171573,11 +171585,11 @@ For example, to get tab context:
171573
171585
  var init_knownServers = () => {};
171574
171586
 
171575
171587
  // src/capabilities/mcp/envExpansion.ts
171576
- function expandEnvVarsInString(value) {
171588
+ function expandEnvVarsInString(value, env = process.env) {
171577
171589
  const missingVars = [];
171578
171590
  const expanded = value.replace(/\$\{([^}]+)\}/g, (match, varContent) => {
171579
171591
  const [varName, defaultValue] = varContent.split(":-", 2);
171580
- const envValue = process.env[varName];
171592
+ const envValue = env[varName];
171581
171593
  if (envValue !== undefined) {
171582
171594
  return envValue;
171583
171595
  }
@@ -171593,6 +171605,61 @@ function expandEnvVarsInString(value) {
171593
171605
  };
171594
171606
  }
171595
171607
 
171608
+ // src/capabilities/mcp/codexConfigFields.ts
171609
+ function isPlainObject6(value) {
171610
+ return !!value && typeof value === "object" && !Array.isArray(value);
171611
+ }
171612
+ function setMissing(record, key, value) {
171613
+ if (record[key] !== undefined)
171614
+ return;
171615
+ record[key] = value;
171616
+ }
171617
+ function recordFor(config, key) {
171618
+ const existing = config[key];
171619
+ if (isPlainObject6(existing))
171620
+ return existing;
171621
+ const created = {};
171622
+ config[key] = created;
171623
+ return created;
171624
+ }
171625
+ function stringRecordEntries(value) {
171626
+ if (!isPlainObject6(value))
171627
+ return [];
171628
+ return Object.entries(value).filter((entry) => typeof entry[1] === "string");
171629
+ }
171630
+ function normalizeCodexMcpFields(input) {
171631
+ if (!isPlainObject6(input))
171632
+ return {};
171633
+ const config = structuredClone(input);
171634
+ const envVars = config["env_vars"];
171635
+ if (Array.isArray(envVars)) {
171636
+ for (const name of envVars.filter((item) => typeof item === "string")) {
171637
+ setMissing(recordFor(config, "env"), name, `\${${name}}`);
171638
+ }
171639
+ delete config["env_vars"];
171640
+ }
171641
+ const envHttpHeaders = config["env_http_headers"];
171642
+ if (isPlainObject6(envHttpHeaders)) {
171643
+ for (const [header, varName] of stringRecordEntries(envHttpHeaders)) {
171644
+ setMissing(recordFor(config, "headers"), header, `\${${varName}}`);
171645
+ }
171646
+ delete config["env_http_headers"];
171647
+ }
171648
+ const bearer = config["bearer_token_env_var"];
171649
+ if (typeof bearer === "string" && bearer.trim()) {
171650
+ setMissing(recordFor(config, "headers"), "Authorization", `Bearer \${${bearer.trim()}}`);
171651
+ delete config["bearer_token_env_var"];
171652
+ }
171653
+ const httpHeaders = config["http_headers"];
171654
+ if (isPlainObject6(httpHeaders)) {
171655
+ for (const [header, value] of stringRecordEntries(httpHeaders)) {
171656
+ setMissing(recordFor(config, "headers"), header, value);
171657
+ }
171658
+ delete config["http_headers"];
171659
+ }
171660
+ return config;
171661
+ }
171662
+
171596
171663
  // src/capabilities/plugins/mcpPluginIntegration.ts
171597
171664
  import { join as join40 } from "path";
171598
171665
  async function loadMcpServersFromMcpb(plugin, mcpbPath, errors) {
@@ -171725,7 +171792,7 @@ async function loadMcpServersFromFile(pluginPath, relativePath, errors, plugin,
171725
171792
  const mcpServers = parsed.mcpServers || parsed;
171726
171793
  const validatedServers = {};
171727
171794
  for (const [name, config] of Object.entries(mcpServers)) {
171728
- const result = McpServerConfigSchema().safeParse(config);
171795
+ const result = McpServerConfigSchema().safeParse(normalizeCodexMcpFields(config));
171729
171796
  if (result.success) {
171730
171797
  validatedServers[name] = result.data;
171731
171798
  } else {
@@ -171783,12 +171850,13 @@ function buildMcpUserConfig(plugin, serverName) {
171783
171850
  }
171784
171851
  function resolvePluginMcpEnvironment(config, plugin, userConfig, errors, pluginName, serverName) {
171785
171852
  const allMissingVars = [];
171853
+ const envLookup = plugin.env ? { ...process.env, ...plugin.env } : process.env;
171786
171854
  const resolveValue = (value) => {
171787
171855
  let resolved = substitutePluginVariables(value, plugin);
171788
171856
  if (userConfig) {
171789
171857
  resolved = substituteUserConfigVariables(resolved, userConfig);
171790
171858
  }
171791
- const { expanded, missingVars } = expandEnvVarsInString(resolved);
171859
+ const { expanded, missingVars } = expandEnvVarsInString(resolved, envLookup);
171792
171860
  allMissingVars.push(...missingVars);
171793
171861
  return expanded;
171794
171862
  };
@@ -193304,12 +193372,13 @@ async function loadLspServersFromManifest(declaration, pluginPath, pluginName, e
193304
193372
  }
193305
193373
  function resolvePluginLspEnvironment(config, plugin, userConfig, _errors) {
193306
193374
  const allMissingVars = [];
193375
+ const envLookup = plugin.env ? { ...process.env, ...plugin.env } : process.env;
193307
193376
  const resolveValue = (value) => {
193308
193377
  let resolved = substitutePluginVariables(value, plugin);
193309
193378
  if (userConfig) {
193310
193379
  resolved = substituteUserConfigVariables(resolved, userConfig);
193311
193380
  }
193312
- const { expanded, missingVars } = expandEnvVarsInString(resolved);
193381
+ const { expanded, missingVars } = expandEnvVarsInString(resolved, envLookup);
193313
193382
  allMissingVars.push(...missingVars);
193314
193383
  return expanded;
193315
193384
  };
@@ -227327,7 +227396,7 @@ function getAnthropicEnvMetadata() {
227327
227396
  function getBuildAgeMinutes() {
227328
227397
  if (false)
227329
227398
  ;
227330
- const buildTime = new Date("2026-10-09T14:01:34.561Z").getTime();
227399
+ const buildTime = new Date("2026-10-19T06:49:44.552Z").getTime();
227331
227400
  if (isNaN(buildTime))
227332
227401
  return;
227333
227402
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -231551,7 +231620,7 @@ async function getAttachments(input, toolUseContext, ideSelection, queuedCommand
231551
231620
  maybe("changed_files", () => getChangedFiles(context)),
231552
231621
  maybe("nested_memory", () => getNestedMemoryAttachments(context)),
231553
231622
  maybe("dynamic_skill", () => getDynamicSkillAttachments(context)),
231554
- maybe("skill_listing", () => getSkillListingAttachments(context)),
231623
+ ...isSkillListingEnabled() ? [maybe("skill_listing", () => getSkillListingAttachments(context))] : [],
231555
231624
  maybe("plan_mode", () => getPlanModeAttachments(messages, toolUseContext)),
231556
231625
  maybe("plan_mode_exit", () => getPlanModeExitAttachment(toolUseContext)),
231557
231626
  ...[],
@@ -232380,6 +232449,9 @@ async function getDynamicSkillAttachments(toolUseContext) {
232380
232449
  }
232381
232450
  return attachments;
232382
232451
  }
232452
+ function isSkillListingEnabled() {
232453
+ return !isEnvDefinedFalsy(resolveEnvVar("SKILL_LISTING"));
232454
+ }
232383
232455
  async function getSkillListingAttachments(toolUseContext) {
232384
232456
  if (false) {}
232385
232457
  if (!toolUseContext.options.tools.some((t) => toolMatchesName2(t, SKILL_TOOL_NAME))) {
@@ -278312,10 +278384,15 @@ function assertNoDependencyCycles(plugins) {
278312
278384
  for (const plugin of plugins)
278313
278385
  visit(plugin, []);
278314
278386
  }
278387
+ var HOST_PROVIDED_ENVIRONMENT_NAMES = new Set([
278388
+ "CLAUDE_PLUGIN_ROOT",
278389
+ "CLAUDE_PLUGIN_DATA"
278390
+ ]);
278315
278391
  function collectEnvironmentNames(value, output) {
278316
278392
  if (typeof value === "string") {
278317
278393
  for (const match of value.matchAll(/\$\{([A-Z][A-Z0-9_]*)\}/g)) {
278318
- output.add(match[1]);
278394
+ if (!HOST_PROVIDED_ENVIRONMENT_NAMES.has(match[1]))
278395
+ output.add(match[1]);
278319
278396
  }
278320
278397
  return;
278321
278398
  }
@@ -278328,8 +278405,10 @@ function collectEnvironmentNames(value, output) {
278328
278405
  return;
278329
278406
  for (const [key, item] of Object.entries(value)) {
278330
278407
  if (key === "env" && item && typeof item === "object" && !Array.isArray(item)) {
278331
- for (const name of Object.keys(item))
278332
- output.add(name);
278408
+ for (const name of Object.keys(item)) {
278409
+ if (!HOST_PROVIDED_ENVIRONMENT_NAMES.has(name))
278410
+ output.add(name);
278411
+ }
278333
278412
  }
278334
278413
  collectEnvironmentNames(item, output);
278335
278414
  }
@@ -278458,6 +278537,8 @@ async function stageResolvedPlugins(revision, inputs) {
278458
278537
  plugin.source = input.id;
278459
278538
  plugin.repository = input.id;
278460
278539
  plugin.dataPath = dataPath;
278540
+ if (input.env && Object.keys(input.env).length > 0)
278541
+ plugin.env = { ...input.env };
278461
278542
  plugin.manifest = { ...plugin.manifest, userConfig: undefined };
278462
278543
  loadedPlugins.push(plugin);
278463
278544
  diagnostics.push(...errors.map((error) => diagnosticFromPluginError(input.id, error)));
@@ -278596,7 +278677,8 @@ async function stageManagedPluginSnapshot(snapshot, appliedState) {
278596
278677
  id: plugin.id,
278597
278678
  path: plugin.source.path,
278598
278679
  dataPath: plugin.dataPath,
278599
- permissionGrant: plugin.permissionGrant
278680
+ permissionGrant: plugin.permissionGrant,
278681
+ env: plugin.env
278600
278682
  })));
278601
278683
  return {
278602
278684
  status: "staged",
@@ -278842,7 +278924,8 @@ function optionsWithProviderRoutingEnv(options) {
278842
278924
  const reasoningEffort = options.reasoningEffort;
278843
278925
  const openaiResponses = options.providerSpecific?.openaiResponses;
278844
278926
  const extraBody = extraBodyFromOptions(options);
278845
- if (transport === undefined && reasoningEffort === undefined && openaiResponses === undefined && extraBody === undefined) {
278927
+ const skillListing = options.skillListing;
278928
+ if (transport === undefined && reasoningEffort === undefined && openaiResponses === undefined && extraBody === undefined && skillListing === undefined) {
278846
278929
  return options;
278847
278930
  }
278848
278931
  const mergedEnv = { ...options.env ?? {} };
@@ -278862,6 +278945,9 @@ function optionsWithProviderRoutingEnv(options) {
278862
278945
  mergedEnv[ENV_VARS.EXTRA_BODY.primary] = rawExtraBody;
278863
278946
  mergedEnv[QUERY_ENV_KEY_EXTRA_BODY] = rawExtraBody;
278864
278947
  }
278948
+ if (typeof skillListing === "boolean") {
278949
+ mergedEnv[ENV_VARS.SKILL_LISTING.primary] = skillListing ? "1" : "0";
278950
+ }
278865
278951
  return { ...options, env: mergedEnv };
278866
278952
  }
278867
278953
  function createQueryLike(generator, runtimeState, finalize) {
@@ -280498,4 +280584,4 @@ export {
280498
280584
  watchScheduledTasks
280499
280585
  };
280500
280586
 
280501
- //# debugId=7505C50D413CC12764756E2164756E21
280587
+ //# debugId=E5F427337E592C6D64756E2164756E21
@@ -236,6 +236,7 @@ export declare const ENV_VARS: {
236
236
  readonly DISABLE_EARLY_INPUT: EnvVarSpec;
237
237
  readonly ENABLE_EXTENDED_KEYS: EnvVarSpec;
238
238
  readonly ENABLE_STREAM_WATCHDOG: EnvVarSpec;
239
+ readonly SKILL_LISTING: EnvVarSpec;
239
240
  readonly STREAM_IDLE_TIMEOUT_MS: EnvVarSpec;
240
241
  readonly UNBOUNDED_CONNECTION_RETRY: EnvVarSpec;
241
242
  readonly STREAM_REPLAY_RETRIES: EnvVarSpec;
@@ -149,6 +149,12 @@ export type SdkManagedPlugin = {
149
149
  contentHash: string;
150
150
  dataPath: string;
151
151
  permissionGrant: SdkPluginPermissionGrant;
152
+ /**
153
+ * Host-supplied runtime environment values for this plugin's components
154
+ * (e.g. a third-party token the user entered in the host UI). Values take
155
+ * precedence over the ambient process env and stay scoped to this plugin.
156
+ */
157
+ env?: Record<string, string>;
152
158
  };
153
159
  export type SdkPluginSnapshot = {
154
160
  revision: string;
@@ -362,6 +368,16 @@ export type Options = {
362
368
  * query startup never blocks on host skill resolution.
363
369
  */
364
370
  skillCommandsProvider?: () => Promise<readonly unknown[]>;
371
+ /**
372
+ * 技能清单附件(`skill_listing`)总开关,默认 `true`(行为与未加开关前一致)。
373
+ *
374
+ * 置 `false` 后 SDK 不再把技能清单作为附件挂到用户消息上——用于 host 自己在
375
+ * 系统提示里维护技能目录、并自行通知技能增删变化的场景。技能正文仍走 Skill
376
+ * 工具按名加载,不受影响。
377
+ *
378
+ * 进程级等价物为 `OPENCOW_SKILL_LISTING=0`;per-query 的显式选项优先于进程环境。
379
+ */
380
+ skillListing?: boolean;
365
381
  /**
366
382
  * Phase 1B.11d — host-provided agent definitions. Each entry is merged
367
383
  * into the internal agent list used by the built-in AgentTool. Type is
package/dist/sdk.js CHANGED
@@ -19269,6 +19269,7 @@ var init_envVars = __esm(() => {
19269
19269
  DISABLE_EARLY_INPUT: envVar("OPENCOW_DISABLE_EARLY_INPUT"),
19270
19270
  ENABLE_EXTENDED_KEYS: envVar("OPENCOW_ENABLE_EXTENDED_KEYS"),
19271
19271
  ENABLE_STREAM_WATCHDOG: envVar("OPENCOW_ENABLE_STREAM_WATCHDOG"),
19272
+ SKILL_LISTING: envVar("OPENCOW_SKILL_LISTING"),
19272
19273
  STREAM_IDLE_TIMEOUT_MS: envVar("OPENCOW_STREAM_IDLE_TIMEOUT_MS"),
19273
19274
  UNBOUNDED_CONNECTION_RETRY: envVar("OPENCOW_UNBOUNDED_CONNECTION_RETRY"),
19274
19275
  STREAM_REPLAY_RETRIES: envVar("OPENCOW_STREAM_REPLAY_RETRIES"),
@@ -19543,6 +19544,12 @@ var init_paths = __esm(() => {
19543
19544
  });
19544
19545
 
19545
19546
  // src/session/layout/LayoutProfile.ts
19547
+ var exports_LayoutProfile = {};
19548
+ __export(exports_LayoutProfile, {
19549
+ DEFAULT_LAYOUT_PROFILE: () => DEFAULT_LAYOUT_PROFILE,
19550
+ resolveBinding: () => resolveBinding,
19551
+ resolveLayoutProfile: () => resolveLayoutProfile
19552
+ });
19546
19553
  function resolveBinding(binding) {
19547
19554
  if (typeof binding === "string") {
19548
19555
  return BUILT_IN_BINDINGS[binding];
@@ -27244,6 +27251,11 @@ var init_platform = __esm(() => {
27244
27251
  });
27245
27252
 
27246
27253
  // src/session/settings/managedPath.ts
27254
+ var exports_managedPath = {};
27255
+ __export(exports_managedPath, {
27256
+ getManagedFilePath: () => getManagedFilePath,
27257
+ getManagedSettingsDropInDir: () => getManagedSettingsDropInDir
27258
+ });
27247
27259
  import { join as join5 } from "path";
27248
27260
  var getManagedFilePath, getManagedSettingsDropInDir;
27249
27261
  var init_managedPath = __esm(() => {
@@ -90142,8 +90154,8 @@ function createBootQueryContext() {
90142
90154
  }
90143
90155
  const sessionId = randomUUID();
90144
90156
  init_paths();
90145
- init_LayoutProfile();
90146
- init_managedPath();
90157
+ const { DEFAULT_LAYOUT_PROFILE } = (init_LayoutProfile(), __toCommonJS(exports_LayoutProfile));
90158
+ const { getManagedFilePath } = (init_managedPath(), __toCommonJS(exports_managedPath));
90147
90159
  const { homedir } = __require("node:os");
90148
90160
  const bootConfigHomeDir = process.env.CLAUDE_CONFIG_DIR ? process.env.CLAUDE_CONFIG_DIR.normalize("NFC") : (homedir() + "/.opencow").normalize("NFC");
90149
90161
  const bootMemoryBaseDir = resolveEnvVar("REMOTE_MEMORY_DIR") ?? bootConfigHomeDir;
@@ -90491,7 +90503,7 @@ function buildQueryContextFromSdk(options) {
90491
90503
  const memoryBaseDir = resolveEnvVar("REMOTE_MEMORY_DIR") ?? configHomeDir;
90492
90504
  const autoMemEnvOverride = resolveEnvVar("MEMORY_PATH_OVERRIDE");
90493
90505
  const autoMemSettingsOverride = resolvedFileSystemLayer === "disabled" ? undefined : readAutoMemSettingsOverride();
90494
- init_managedPath();
90506
+ const { getManagedFilePath } = (init_managedPath(), __toCommonJS(exports_managedPath));
90495
90507
  const paths = computeSessionPaths({
90496
90508
  cwd,
90497
90509
  projectRoot: cwd,
@@ -171573,11 +171585,11 @@ For example, to get tab context:
171573
171585
  var init_knownServers = () => {};
171574
171586
 
171575
171587
  // src/capabilities/mcp/envExpansion.ts
171576
- function expandEnvVarsInString(value) {
171588
+ function expandEnvVarsInString(value, env = process.env) {
171577
171589
  const missingVars = [];
171578
171590
  const expanded = value.replace(/\$\{([^}]+)\}/g, (match, varContent) => {
171579
171591
  const [varName, defaultValue] = varContent.split(":-", 2);
171580
- const envValue = process.env[varName];
171592
+ const envValue = env[varName];
171581
171593
  if (envValue !== undefined) {
171582
171594
  return envValue;
171583
171595
  }
@@ -171593,6 +171605,61 @@ function expandEnvVarsInString(value) {
171593
171605
  };
171594
171606
  }
171595
171607
 
171608
+ // src/capabilities/mcp/codexConfigFields.ts
171609
+ function isPlainObject6(value) {
171610
+ return !!value && typeof value === "object" && !Array.isArray(value);
171611
+ }
171612
+ function setMissing(record, key, value) {
171613
+ if (record[key] !== undefined)
171614
+ return;
171615
+ record[key] = value;
171616
+ }
171617
+ function recordFor(config, key) {
171618
+ const existing = config[key];
171619
+ if (isPlainObject6(existing))
171620
+ return existing;
171621
+ const created = {};
171622
+ config[key] = created;
171623
+ return created;
171624
+ }
171625
+ function stringRecordEntries(value) {
171626
+ if (!isPlainObject6(value))
171627
+ return [];
171628
+ return Object.entries(value).filter((entry) => typeof entry[1] === "string");
171629
+ }
171630
+ function normalizeCodexMcpFields(input) {
171631
+ if (!isPlainObject6(input))
171632
+ return {};
171633
+ const config = structuredClone(input);
171634
+ const envVars = config["env_vars"];
171635
+ if (Array.isArray(envVars)) {
171636
+ for (const name of envVars.filter((item) => typeof item === "string")) {
171637
+ setMissing(recordFor(config, "env"), name, `\${${name}}`);
171638
+ }
171639
+ delete config["env_vars"];
171640
+ }
171641
+ const envHttpHeaders = config["env_http_headers"];
171642
+ if (isPlainObject6(envHttpHeaders)) {
171643
+ for (const [header, varName] of stringRecordEntries(envHttpHeaders)) {
171644
+ setMissing(recordFor(config, "headers"), header, `\${${varName}}`);
171645
+ }
171646
+ delete config["env_http_headers"];
171647
+ }
171648
+ const bearer = config["bearer_token_env_var"];
171649
+ if (typeof bearer === "string" && bearer.trim()) {
171650
+ setMissing(recordFor(config, "headers"), "Authorization", `Bearer \${${bearer.trim()}}`);
171651
+ delete config["bearer_token_env_var"];
171652
+ }
171653
+ const httpHeaders = config["http_headers"];
171654
+ if (isPlainObject6(httpHeaders)) {
171655
+ for (const [header, value] of stringRecordEntries(httpHeaders)) {
171656
+ setMissing(recordFor(config, "headers"), header, value);
171657
+ }
171658
+ delete config["http_headers"];
171659
+ }
171660
+ return config;
171661
+ }
171662
+
171596
171663
  // src/capabilities/plugins/mcpPluginIntegration.ts
171597
171664
  import { join as join40 } from "path";
171598
171665
  async function loadMcpServersFromMcpb(plugin, mcpbPath, errors) {
@@ -171725,7 +171792,7 @@ async function loadMcpServersFromFile(pluginPath, relativePath, errors, plugin,
171725
171792
  const mcpServers = parsed.mcpServers || parsed;
171726
171793
  const validatedServers = {};
171727
171794
  for (const [name, config] of Object.entries(mcpServers)) {
171728
- const result = McpServerConfigSchema().safeParse(config);
171795
+ const result = McpServerConfigSchema().safeParse(normalizeCodexMcpFields(config));
171729
171796
  if (result.success) {
171730
171797
  validatedServers[name] = result.data;
171731
171798
  } else {
@@ -171783,12 +171850,13 @@ function buildMcpUserConfig(plugin, serverName) {
171783
171850
  }
171784
171851
  function resolvePluginMcpEnvironment(config, plugin, userConfig, errors, pluginName, serverName) {
171785
171852
  const allMissingVars = [];
171853
+ const envLookup = plugin.env ? { ...process.env, ...plugin.env } : process.env;
171786
171854
  const resolveValue = (value) => {
171787
171855
  let resolved = substitutePluginVariables(value, plugin);
171788
171856
  if (userConfig) {
171789
171857
  resolved = substituteUserConfigVariables(resolved, userConfig);
171790
171858
  }
171791
- const { expanded, missingVars } = expandEnvVarsInString(resolved);
171859
+ const { expanded, missingVars } = expandEnvVarsInString(resolved, envLookup);
171792
171860
  allMissingVars.push(...missingVars);
171793
171861
  return expanded;
171794
171862
  };
@@ -193304,12 +193372,13 @@ async function loadLspServersFromManifest(declaration, pluginPath, pluginName, e
193304
193372
  }
193305
193373
  function resolvePluginLspEnvironment(config, plugin, userConfig, _errors) {
193306
193374
  const allMissingVars = [];
193375
+ const envLookup = plugin.env ? { ...process.env, ...plugin.env } : process.env;
193307
193376
  const resolveValue = (value) => {
193308
193377
  let resolved = substitutePluginVariables(value, plugin);
193309
193378
  if (userConfig) {
193310
193379
  resolved = substituteUserConfigVariables(resolved, userConfig);
193311
193380
  }
193312
- const { expanded, missingVars } = expandEnvVarsInString(resolved);
193381
+ const { expanded, missingVars } = expandEnvVarsInString(resolved, envLookup);
193313
193382
  allMissingVars.push(...missingVars);
193314
193383
  return expanded;
193315
193384
  };
@@ -227327,7 +227396,7 @@ function getAnthropicEnvMetadata() {
227327
227396
  function getBuildAgeMinutes() {
227328
227397
  if (false)
227329
227398
  ;
227330
- const buildTime = new Date("2026-10-09T14:01:34.561Z").getTime();
227399
+ const buildTime = new Date("2026-10-19T06:49:44.552Z").getTime();
227331
227400
  if (isNaN(buildTime))
227332
227401
  return;
227333
227402
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -231551,7 +231620,7 @@ async function getAttachments(input, toolUseContext, ideSelection, queuedCommand
231551
231620
  maybe("changed_files", () => getChangedFiles(context)),
231552
231621
  maybe("nested_memory", () => getNestedMemoryAttachments(context)),
231553
231622
  maybe("dynamic_skill", () => getDynamicSkillAttachments(context)),
231554
- maybe("skill_listing", () => getSkillListingAttachments(context)),
231623
+ ...isSkillListingEnabled() ? [maybe("skill_listing", () => getSkillListingAttachments(context))] : [],
231555
231624
  maybe("plan_mode", () => getPlanModeAttachments(messages, toolUseContext)),
231556
231625
  maybe("plan_mode_exit", () => getPlanModeExitAttachment(toolUseContext)),
231557
231626
  ...[],
@@ -232380,6 +232449,9 @@ async function getDynamicSkillAttachments(toolUseContext) {
232380
232449
  }
232381
232450
  return attachments;
232382
232451
  }
232452
+ function isSkillListingEnabled() {
232453
+ return !isEnvDefinedFalsy(resolveEnvVar("SKILL_LISTING"));
232454
+ }
232383
232455
  async function getSkillListingAttachments(toolUseContext) {
232384
232456
  if (false) {}
232385
232457
  if (!toolUseContext.options.tools.some((t) => toolMatchesName2(t, SKILL_TOOL_NAME))) {
@@ -278312,10 +278384,15 @@ function assertNoDependencyCycles(plugins) {
278312
278384
  for (const plugin of plugins)
278313
278385
  visit(plugin, []);
278314
278386
  }
278387
+ var HOST_PROVIDED_ENVIRONMENT_NAMES = new Set([
278388
+ "CLAUDE_PLUGIN_ROOT",
278389
+ "CLAUDE_PLUGIN_DATA"
278390
+ ]);
278315
278391
  function collectEnvironmentNames(value, output) {
278316
278392
  if (typeof value === "string") {
278317
278393
  for (const match of value.matchAll(/\$\{([A-Z][A-Z0-9_]*)\}/g)) {
278318
- output.add(match[1]);
278394
+ if (!HOST_PROVIDED_ENVIRONMENT_NAMES.has(match[1]))
278395
+ output.add(match[1]);
278319
278396
  }
278320
278397
  return;
278321
278398
  }
@@ -278328,8 +278405,10 @@ function collectEnvironmentNames(value, output) {
278328
278405
  return;
278329
278406
  for (const [key, item] of Object.entries(value)) {
278330
278407
  if (key === "env" && item && typeof item === "object" && !Array.isArray(item)) {
278331
- for (const name of Object.keys(item))
278332
- output.add(name);
278408
+ for (const name of Object.keys(item)) {
278409
+ if (!HOST_PROVIDED_ENVIRONMENT_NAMES.has(name))
278410
+ output.add(name);
278411
+ }
278333
278412
  }
278334
278413
  collectEnvironmentNames(item, output);
278335
278414
  }
@@ -278458,6 +278537,8 @@ async function stageResolvedPlugins(revision, inputs) {
278458
278537
  plugin.source = input.id;
278459
278538
  plugin.repository = input.id;
278460
278539
  plugin.dataPath = dataPath;
278540
+ if (input.env && Object.keys(input.env).length > 0)
278541
+ plugin.env = { ...input.env };
278461
278542
  plugin.manifest = { ...plugin.manifest, userConfig: undefined };
278462
278543
  loadedPlugins.push(plugin);
278463
278544
  diagnostics.push(...errors.map((error) => diagnosticFromPluginError(input.id, error)));
@@ -278596,7 +278677,8 @@ async function stageManagedPluginSnapshot(snapshot, appliedState) {
278596
278677
  id: plugin.id,
278597
278678
  path: plugin.source.path,
278598
278679
  dataPath: plugin.dataPath,
278599
- permissionGrant: plugin.permissionGrant
278680
+ permissionGrant: plugin.permissionGrant,
278681
+ env: plugin.env
278600
278682
  })));
278601
278683
  return {
278602
278684
  status: "staged",
@@ -278842,7 +278924,8 @@ function optionsWithProviderRoutingEnv(options) {
278842
278924
  const reasoningEffort = options.reasoningEffort;
278843
278925
  const openaiResponses = options.providerSpecific?.openaiResponses;
278844
278926
  const extraBody = extraBodyFromOptions(options);
278845
- if (transport === undefined && reasoningEffort === undefined && openaiResponses === undefined && extraBody === undefined) {
278927
+ const skillListing = options.skillListing;
278928
+ if (transport === undefined && reasoningEffort === undefined && openaiResponses === undefined && extraBody === undefined && skillListing === undefined) {
278846
278929
  return options;
278847
278930
  }
278848
278931
  const mergedEnv = { ...options.env ?? {} };
@@ -278862,6 +278945,9 @@ function optionsWithProviderRoutingEnv(options) {
278862
278945
  mergedEnv[ENV_VARS.EXTRA_BODY.primary] = rawExtraBody;
278863
278946
  mergedEnv[QUERY_ENV_KEY_EXTRA_BODY] = rawExtraBody;
278864
278947
  }
278948
+ if (typeof skillListing === "boolean") {
278949
+ mergedEnv[ENV_VARS.SKILL_LISTING.primary] = skillListing ? "1" : "0";
278950
+ }
278865
278951
  return { ...options, env: mergedEnv };
278866
278952
  }
278867
278953
  function createQueryLike(generator, runtimeState, finalize) {
@@ -280498,4 +280584,4 @@ export {
280498
280584
  watchScheduledTasks
280499
280585
  };
280500
280586
 
280501
- //# debugId=40D178CA130F2D5864756E2164756E21
280587
+ //# debugId=D86F97561ED1E61E64756E2164756E21
@@ -581,6 +581,15 @@ export declare function suppressNextSkillListing(): void;
581
581
  * Falls back to bundled-only if bundled+mcp exceeds FILTERED_LISTING_MAX.
582
582
  */
583
583
  export declare function filterToBundledAndMcp(commands: Command[]): Command[];
584
+ /**
585
+ * skill_listing 附件总开关。Exported for testing。
586
+ *
587
+ * 读取经 optionsWithProviderRoutingEnv 写入 per-query env 的
588
+ * Options.skillListing(per-query 优先于进程环境);未设置时默认开启,
589
+ * 保持加开关前的行为不变。仅识别显式 falsy('0'/'false'/'no'/'off'),
590
+ * 与 shouldInjectAgentListInMessages 的语义一致。
591
+ */
592
+ export declare function isSkillListingEnabled(): boolean;
584
593
  export declare function extractAtMentionedFiles(content: string): string[];
585
594
  export declare function extractMcpResourceMentions(content: string): string[];
586
595
  export declare function extractAgentMentions(content: string): string[];
@@ -44,6 +44,11 @@ export type LoadedPlugin = {
44
44
  repository: string;
45
45
  /** Session-owned data directory for explicitly managed plugins. */
46
46
  dataPath?: string;
47
+ /**
48
+ * 宿主为该插件提供的运行期环境变量值(如用户在插件详情页填写的第三方 Token)。
49
+ * 解析 `${VAR}` 时优先于进程环境,且只作用于该插件的组件。
50
+ */
51
+ env?: Record<string, string>;
47
52
  enabled?: boolean;
48
53
  isBuiltin?: boolean;
49
54
  sha?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencow-ai/opencow-agent-sdk",
3
- "version": "0.4.25",
3
+ "version": "0.4.26",
4
4
  "description": "Claude Code opened to any LLM — OpenAI, Gemini, DeepSeek, Ollama, and 200+ models",
5
5
  "type": "module",
6
6
  "main": "./dist/sdk.js",