@bman654/clodex 2.12.0 → 2.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -382,7 +382,7 @@ import { join } from "path";
382
382
  // package.json
383
383
  var package_default = {
384
384
  name: "@bman654/clodex",
385
- version: "2.12.0",
385
+ version: "2.12.2",
386
386
  publishConfig: {
387
387
  access: "public"
388
388
  },
@@ -3884,16 +3884,17 @@ function applyClodexPatches(source, config) {
3884
3884
  // src/patcher.ts
3885
3885
  import { createHash as createHash9 } from "crypto";
3886
3886
  import {
3887
+ chmodSync as chmodSync4,
3887
3888
  copyFileSync,
3888
3889
  existsSync as existsSync5,
3889
3890
  mkdtempSync,
3890
3891
  mkdirSync as mkdirSync5,
3891
3892
  readFileSync as readFileSync10,
3892
- renameSync as renameSync2,
3893
- rmSync,
3893
+ renameSync as renameSync3,
3894
+ rmSync as rmSync2,
3894
3895
  statSync as statSync8,
3895
3896
  unlinkSync as unlinkSync3,
3896
- writeFileSync as writeFileSync5,
3897
+ writeFileSync as writeFileSync6,
3897
3898
  openSync as openSync7,
3898
3899
  closeSync as closeSync7,
3899
3900
  realpathSync as realpathSync2
@@ -4820,16 +4821,19 @@ var pricing_cache_default = {
4820
4821
  ]
4821
4822
  };
4822
4823
 
4824
+ // src/data/opencode-go-models.ts
4825
+ import { randomUUID as randomUUID2 } from "crypto";
4826
+
4823
4827
  // src/data/opencode-go-models.json
4824
4828
  var opencode_go_models_default = [
4825
4829
  {
4826
4830
  id: "deepseek-v4-flash",
4827
- name: "DeepSeek V4 Flash (2x usage)",
4831
+ name: "DeepSeek V4 Flash",
4828
4832
  contextWindow: 1e6,
4829
4833
  cost: {
4830
- input: 0.07,
4831
- output: 0.14,
4832
- cache_read: 14e-4
4834
+ input: 0.15,
4835
+ output: 0.6,
4836
+ cache_read: 3e-3
4833
4837
  },
4834
4838
  modelFormat: "openai",
4835
4839
  npm: "@ai-sdk/openai-compatible",
@@ -4857,12 +4861,12 @@ var opencode_go_models_default = [
4857
4861
  },
4858
4862
  {
4859
4863
  id: "deepseek-v4-pro",
4860
- name: "DeepSeek V4 Pro",
4864
+ name: "DeepSeek V4 Pro (New)",
4861
4865
  contextWindow: 1e6,
4862
4866
  cost: {
4863
- input: 0.435,
4864
- output: 0.87,
4865
- cache_read: 3625e-6
4867
+ input: 0.66,
4868
+ output: 1.98,
4869
+ cache_read: 0.022
4866
4870
  },
4867
4871
  modelFormat: "openai",
4868
4872
  npm: "@ai-sdk/openai-compatible",
@@ -4888,6 +4892,30 @@ var opencode_go_models_default = [
4888
4892
  upstreamModelId: "deepseek-v4-pro",
4889
4893
  family: "deepseek"
4890
4894
  },
4895
+ {
4896
+ id: "deepseek-v4.1-flash",
4897
+ name: "DeepSeek V4.1 Flash",
4898
+ contextWindow: 1e6,
4899
+ cost: {
4900
+ input: 0.15,
4901
+ output: 0.6,
4902
+ cache_read: 3e-3
4903
+ },
4904
+ modelFormat: "anthropic",
4905
+ npm: "@ai-sdk/anthropic",
4906
+ apiUrl: "https://opencode.ai/zen/go",
4907
+ reasoning: true,
4908
+ modalities: [
4909
+ "text",
4910
+ "image"
4911
+ ],
4912
+ compatibility: {
4913
+ supportsCountTokens: false,
4914
+ supportsReasoningEffort: false
4915
+ },
4916
+ upstreamModelId: "deepseek-v4.1-flash",
4917
+ family: "deepseek"
4918
+ },
4891
4919
  {
4892
4920
  id: "glm-5.1",
4893
4921
  name: "GLM-5.1",
@@ -4905,10 +4933,10 @@ var opencode_go_models_default = [
4905
4933
  "text"
4906
4934
  ],
4907
4935
  compatibility: {
4936
+ supportsReasoningEffort: false,
4908
4937
  supportsStore: false,
4909
4938
  supportsDeveloperRole: false,
4910
- maxTokensField: "max_tokens",
4911
- supportsReasoningEffort: false
4939
+ maxTokensField: "max_tokens"
4912
4940
  },
4913
4941
  upstreamModelId: "glm-5.1",
4914
4942
  family: "glm"
@@ -4948,13 +4976,13 @@ var opencode_go_models_default = [
4948
4976
  },
4949
4977
  {
4950
4978
  id: "gpt-5.6-luna",
4951
- name: "GPT-5.6 Luna (2x usage)",
4979
+ name: "GPT-5.6 Luna",
4952
4980
  contextWindow: 105e4,
4953
4981
  cost: {
4954
- input: 0.1,
4955
- output: 0.6,
4956
- cache_read: 0.01,
4957
- cache_write: 0.125
4982
+ input: 0.2,
4983
+ output: 1.2,
4984
+ cache_read: 0.02,
4985
+ cache_write: 0.25
4958
4986
  },
4959
4987
  modelFormat: "openai",
4960
4988
  npm: "@ai-sdk/openai-compatible",
@@ -4964,8 +4992,6 @@ var opencode_go_models_default = [
4964
4992
  "text",
4965
4993
  "image"
4966
4994
  ],
4967
- upstreamModelId: "gpt-5.6-luna",
4968
- family: "gpt",
4969
4995
  compatibility: {
4970
4996
  reasoningEffortMap: {
4971
4997
  off: "none",
@@ -4979,7 +5005,9 @@ var opencode_go_models_default = [
4979
5005
  supportsStore: false,
4980
5006
  supportsDeveloperRole: false,
4981
5007
  maxTokensField: "max_tokens"
4982
- }
5008
+ },
5009
+ upstreamModelId: "gpt-5.6-luna",
5010
+ family: "gpt"
4983
5011
  },
4984
5012
  {
4985
5013
  id: "hy3",
@@ -5065,10 +5093,10 @@ var opencode_go_models_default = [
5065
5093
  "image"
5066
5094
  ],
5067
5095
  compatibility: {
5096
+ supportsReasoningEffort: false,
5068
5097
  supportsStore: false,
5069
5098
  supportsDeveloperRole: false,
5070
- maxTokensField: "max_tokens",
5071
- supportsReasoningEffort: false
5099
+ maxTokensField: "max_tokens"
5072
5100
  },
5073
5101
  upstreamModelId: "kimi-k2.7-code",
5074
5102
  family: "kimi"
@@ -5125,10 +5153,10 @@ var opencode_go_models_default = [
5125
5153
  "image"
5126
5154
  ],
5127
5155
  compatibility: {
5156
+ supportsReasoningEffort: false,
5128
5157
  supportsStore: false,
5129
5158
  supportsDeveloperRole: false,
5130
- maxTokensField: "max_tokens",
5131
- supportsReasoningEffort: false
5159
+ maxTokensField: "max_tokens"
5132
5160
  },
5133
5161
  upstreamModelId: "mimo-v2.5",
5134
5162
  family: "mimo"
@@ -5150,10 +5178,10 @@ var opencode_go_models_default = [
5150
5178
  "text"
5151
5179
  ],
5152
5180
  compatibility: {
5181
+ supportsReasoningEffort: false,
5153
5182
  supportsStore: false,
5154
5183
  supportsDeveloperRole: false,
5155
- maxTokensField: "max_tokens",
5156
- supportsReasoningEffort: false
5184
+ maxTokensField: "max_tokens"
5157
5185
  },
5158
5186
  upstreamModelId: "mimo-v2.5-pro",
5159
5187
  family: "mimo"
@@ -5175,10 +5203,10 @@ var opencode_go_models_default = [
5175
5203
  "text"
5176
5204
  ],
5177
5205
  compatibility: {
5206
+ supportsReasoningEffort: false,
5178
5207
  supportsStore: false,
5179
5208
  supportsDeveloperRole: false,
5180
- maxTokensField: "max_tokens",
5181
- supportsReasoningEffort: false
5209
+ maxTokensField: "max_tokens"
5182
5210
  },
5183
5211
  upstreamModelId: "minimax-m2.7",
5184
5212
  family: "minimax"
@@ -5200,12 +5228,12 @@ var opencode_go_models_default = [
5200
5228
  "text",
5201
5229
  "image"
5202
5230
  ],
5203
- upstreamModelId: "minimax-m3",
5204
- family: "minimax",
5205
5231
  compatibility: {
5206
5232
  supportsCountTokens: false,
5207
5233
  supportsReasoningEffort: false
5208
- }
5234
+ },
5235
+ upstreamModelId: "minimax-m3",
5236
+ family: "minimax"
5209
5237
  },
5210
5238
  {
5211
5239
  id: "qwen3.6-plus",
@@ -5226,10 +5254,6 @@ var opencode_go_models_default = [
5226
5254
  "image"
5227
5255
  ],
5228
5256
  compatibility: {
5229
- supportsStore: false,
5230
- supportsDeveloperRole: false,
5231
- thinkingFormat: "qwen",
5232
- maxTokensField: "max_tokens",
5233
5257
  reasoningEffortMap: {
5234
5258
  low: "low",
5235
5259
  medium: "medium",
@@ -5238,7 +5262,11 @@ var opencode_go_models_default = [
5238
5262
  max: "max",
5239
5263
  minimal: null,
5240
5264
  off: null
5241
- }
5265
+ },
5266
+ supportsStore: false,
5267
+ supportsDeveloperRole: false,
5268
+ thinkingFormat: "qwen",
5269
+ maxTokensField: "max_tokens"
5242
5270
  },
5243
5271
  upstreamModelId: "qwen3.6-plus",
5244
5272
  family: "qwen3.6"
@@ -5260,12 +5288,12 @@ var opencode_go_models_default = [
5260
5288
  modalities: [
5261
5289
  "text"
5262
5290
  ],
5263
- upstreamModelId: "qwen3.7-max",
5264
- family: "qwen3.7",
5265
5291
  compatibility: {
5266
5292
  supportsCountTokens: false,
5267
5293
  supportsReasoningEffort: false
5268
- }
5294
+ },
5295
+ upstreamModelId: "qwen3.7-max",
5296
+ family: "qwen3.7"
5269
5297
  },
5270
5298
  {
5271
5299
  id: "qwen3.7-plus",
@@ -5285,12 +5313,12 @@ var opencode_go_models_default = [
5285
5313
  "text",
5286
5314
  "image"
5287
5315
  ],
5288
- upstreamModelId: "qwen3.7-plus",
5289
- family: "qwen3.7",
5290
5316
  compatibility: {
5291
5317
  supportsCountTokens: false,
5292
5318
  supportsReasoningEffort: false
5293
- }
5319
+ },
5320
+ upstreamModelId: "qwen3.7-plus",
5321
+ family: "qwen3.7"
5294
5322
  },
5295
5323
  {
5296
5324
  id: "qwen3.8-max",
@@ -5310,12 +5338,12 @@ var opencode_go_models_default = [
5310
5338
  "text",
5311
5339
  "image"
5312
5340
  ],
5313
- upstreamModelId: "qwen3.8-max",
5314
- family: "qwen3.8",
5315
5341
  compatibility: {
5316
5342
  supportsCountTokens: false,
5317
5343
  supportsReasoningEffort: false
5318
- }
5344
+ },
5345
+ upstreamModelId: "qwen3.8-max",
5346
+ family: "qwen3.8"
5319
5347
  }
5320
5348
  ];
5321
5349
 
@@ -5327,6 +5355,13 @@ var OPENCODE_GO_ANTHROPIC_BASE_URL = "https://opencode.ai/zen/go";
5327
5355
  function buildOpenCodeGoModels() {
5328
5356
  return structuredClone(opencode_go_models_default);
5329
5357
  }
5358
+ var OPENCODE_GO_FALLBACK_SESSION_ID = randomUUID2();
5359
+ function openCodeGoSessionHeaders(model, claudeSessionId) {
5360
+ const url = String(model.apiBaseUrl ?? model.baseUrl ?? model.apiUrl ?? "");
5361
+ const isGo = model.providerId === OPENCODE_GO_PROVIDER_ID || /^https:\/\/opencode\.ai\/zen\/go(\/|$)/.test(url);
5362
+ if (!isGo) return void 0;
5363
+ return { "x-opencode-session": claudeSessionId ?? OPENCODE_GO_FALLBACK_SESSION_ID };
5364
+ }
5330
5365
 
5331
5366
  // src/provider-templates.ts
5332
5367
  async function verifyOpenCodeGoCredential(apiKey) {
@@ -5337,7 +5372,12 @@ async function verifyOpenCodeGoCredential(apiKey) {
5337
5372
  method: "POST",
5338
5373
  headers: {
5339
5374
  "Content-Type": "application/json",
5340
- Authorization: `Bearer ${apiKey}`
5375
+ Authorization: `Bearer ${apiKey}`,
5376
+ // Go answers `MissingSessionID` to any request without a session header,
5377
+ // and whether it validates the key BEFORE that check is not something we
5378
+ // have observed. Sending the header removes the question: the probe then
5379
+ // cannot mistake a missing-session rejection for a verdict on the key.
5380
+ ...openCodeGoSessionHeaders({ providerId: OPENCODE_GO_PROVIDER_ID }, void 0)
5341
5381
  },
5342
5382
  body: JSON.stringify({ model: model.upstreamModelId ?? model.id }),
5343
5383
  // This is the FIRST thing a user hits after pasting a key, and it runs
@@ -7772,7 +7812,7 @@ function writeAll(fd, bytes, position, path) {
7772
7812
 
7773
7813
  // src/patch-backup.ts
7774
7814
  import { createHash as createHash5 } from "crypto";
7775
- import { existsSync as existsSync4, readFileSync as readFileSync9, readdirSync, statSync as statSync7 } from "fs";
7815
+ import { existsSync as existsSync4, readFileSync as readFileSync9, readdirSync, renameSync as renameSync2, rmSync, statSync as statSync7, writeFileSync as writeFileSync5 } from "fs";
7776
7816
  import { homedir as homedir2 } from "os";
7777
7817
  import { join as join7 } from "path";
7778
7818
  var BACKUP_SHA_PREFIX_LENGTH = 16;
@@ -7790,9 +7830,60 @@ function backupVersionTag(version) {
7790
7830
  function contentAddressedBackupPath(version, sha256, dir = backupDir()) {
7791
7831
  return join7(dir, `claude-${backupVersionTag(version)}-${sha256.slice(0, BACKUP_SHA_PREFIX_LENGTH)}.orig`);
7792
7832
  }
7833
+ function legacyBackupPath(version, dir = backupDir()) {
7834
+ return join7(dir, `claude-${backupVersionTag(version)}.orig`);
7835
+ }
7793
7836
  function tweakccMirrorBackupPath(dir = backupDir()) {
7794
7837
  return join7(dir, "native-binary.backup");
7795
7838
  }
7839
+ function installProvenancePath(backupPath, binaryPath) {
7840
+ const tag = createHash5("sha256").update(binaryPath).digest("hex").slice(0, BACKUP_SHA_PREFIX_LENGTH);
7841
+ return `${backupPath}.for-${tag}.json`;
7842
+ }
7843
+ function installProvenancePattern(name) {
7844
+ return new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\.for-[0-9a-f]{${BACKUP_SHA_PREFIX_LENGTH}}\\.json$`);
7845
+ }
7846
+ function readInstallProvenance(path) {
7847
+ let raw;
7848
+ try {
7849
+ raw = readFileSync9(path, "utf8");
7850
+ } catch {
7851
+ return null;
7852
+ }
7853
+ let parsed;
7854
+ try {
7855
+ parsed = JSON.parse(raw);
7856
+ } catch {
7857
+ return "damaged";
7858
+ }
7859
+ if (typeof parsed !== "object" || parsed === null) return "damaged";
7860
+ const { install, assumed } = parsed;
7861
+ if (typeof install !== "string" || install === "") return "damaged";
7862
+ if (typeof assumed !== "boolean") return "damaged";
7863
+ return { install, assumed };
7864
+ }
7865
+ function recordBackupProvenance(backupPath, binaryPath, opts) {
7866
+ const target = installProvenancePath(backupPath, binaryPath);
7867
+ const existing = readInstallProvenance(target);
7868
+ if (existing && existing !== "damaged" && existing.install === binaryPath) {
7869
+ if (existing.assumed === opts.assumed) return existing.assumed ? "assumed" : "established";
7870
+ if (!existing.assumed) return "established";
7871
+ }
7872
+ const record = { install: binaryPath, assumed: opts.assumed };
7873
+ const temp = `${target}.tmp-${process.pid}-${Date.now().toString(36)}`;
7874
+ try {
7875
+ writeFileSync5(temp, `${JSON.stringify(record, null, 2)}
7876
+ `);
7877
+ renameSync2(temp, target);
7878
+ return opts.assumed ? "assumed" : "established";
7879
+ } catch (err) {
7880
+ try {
7881
+ rmSync(temp, { force: true });
7882
+ } catch {
7883
+ }
7884
+ throw err;
7885
+ }
7886
+ }
7796
7887
  function scanPristineBackups(version, dir = backupDir()) {
7797
7888
  const tag = backupVersionTag(version);
7798
7889
  const pattern = new RegExp(`^claude-${tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:-([0-9a-f]{${BACKUP_SHA_PREFIX_LENGTH}}))?\\.orig$`);
@@ -7802,6 +7893,27 @@ function scanPristineBackups(version, dir = backupDir()) {
7802
7893
  } catch {
7803
7894
  return { valid: [], corrupt: [] };
7804
7895
  }
7896
+ const provenanceOf = (name) => {
7897
+ const recordPattern = installProvenancePattern(name);
7898
+ const installs = [];
7899
+ const assumedInstalls = [];
7900
+ const damagedProvenance = [];
7901
+ for (const entry of entries) {
7902
+ if (!recordPattern.test(entry)) continue;
7903
+ const path = join7(dir, entry);
7904
+ const record = readInstallProvenance(path);
7905
+ if (record === null || record === "damaged" || installProvenancePath(join7(dir, name), record.install) !== path) {
7906
+ damagedProvenance.push(path);
7907
+ continue;
7908
+ }
7909
+ (record.assumed ? assumedInstalls : installs).push(record.install);
7910
+ }
7911
+ return {
7912
+ installs: installs.sort(),
7913
+ assumedInstalls: assumedInstalls.sort(),
7914
+ damagedProvenance: damagedProvenance.sort()
7915
+ };
7916
+ };
7805
7917
  const valid = [];
7806
7918
  const corrupt = [];
7807
7919
  for (const entry of entries.sort()) {
@@ -7822,9 +7934,9 @@ function scanPristineBackups(version, dir = backupDir()) {
7822
7934
  corrupt.push(path);
7823
7935
  continue;
7824
7936
  }
7825
- valid.push({ path, kind: "content-addressed", sha256 });
7937
+ valid.push({ path, kind: "content-addressed", sha256, ...provenanceOf(entry) });
7826
7938
  } else {
7827
- valid.push({ path, kind: "legacy", sha256 });
7939
+ valid.push({ path, kind: "legacy", sha256, ...provenanceOf(entry) });
7828
7940
  }
7829
7941
  }
7830
7942
  return { valid, corrupt };
@@ -7846,7 +7958,7 @@ function looksLikeLegacyClodexPatch(source) {
7846
7958
  }
7847
7959
  function noBackupMessage(facts) {
7848
7960
  const corrupt = facts.corruptBackups?.length ? ` (${facts.corruptBackups.length} backup file(s) for this version failed integrity checks and were ignored)` : "";
7849
- return `claude ${facts.version} is already patched and no trustworthy pristine backup for that version exists in ${backupDir()}${corrupt}. Reinstall Claude Code to get a pristine binary, then run \`clodex patch\`.`;
7961
+ return `clodex holds no pristine backup of claude ${facts.version} it can attribute to ${facts.binaryPath}: none was found in ${backupDir()}${corrupt}. If that binary is patched, reinstall Claude Code to get a pristine one and run \`clodex patch\` again. If it was never patched, there is nothing to restore.`;
7850
7962
  }
7851
7963
  function identifiesABackup(manifest) {
7852
7964
  return manifest.pristineSha256 !== void 0 || manifest.backupPath !== void 0;
@@ -7860,8 +7972,26 @@ function recordedBackupGoneMessage(facts, manifest) {
7860
7972
  function otherInstallMessage(facts, otherBinaryPath) {
7861
7973
  return `The patch manifest records a different Claude Code install (${otherBinaryPath}), so nothing establishes that a pristine backup tagged claude ${facts.version} in ${backupDir()} belongs to ${facts.binaryPath}. Two installs of one Claude Code version are different files, so restoring by version tag alone would overwrite this install with another one's bytes. Set TWEAKCC_CC_INSTALLATION_PATH=${otherBinaryPath} to restore that install instead, or reinstall Claude Code to make this one pristine.`;
7862
7974
  }
7975
+ function otherInstallProvenanceMessage(facts, recorded) {
7976
+ const owners = [...new Set(recorded.flatMap((backup) => backup.installs))];
7977
+ return `The pristine backup(s) of claude ${facts.version} in ${backupDir()} are recorded as the pristine content of ${owners.join(", ")}, not of ${facts.binaryPath}. Two installs of one Claude Code version are different files, so restoring one of those would overwrite this install with bytes that were never its own. Set TWEAKCC_CC_INSTALLATION_PATH=${owners[0]} to restore that install instead, or reinstall Claude Code to make this one pristine.`;
7978
+ }
7979
+ function damagedProvenanceMessage(facts, damaged) {
7980
+ return `A pristine backup of claude ${facts.version} in ${backupDir()} carries a provenance record that clodex cannot read (${damaged.join(", ")}). Something recorded which install those bytes belong to and that record is damaged, so clodex will not fall back to matching on the claude version in the file name \u2014 on a machine with more than one install that would hand ${facts.binaryPath} bytes that were never its own. Delete the unreadable record to accept that fallback, or reinstall Claude Code to make this install pristine.`;
7981
+ }
7982
+ function contradictoryProvenanceMessage(facts, mine) {
7983
+ return `More than one pristine backup of claude ${facts.version} is recorded as the pristine content of ${facts.binaryPath} (${mine.map((backup) => backup.path).join(", ")}), and they do not hold the same bytes, so clodex cannot tell which one that install holds now \u2014 being snapshotted twice, at two different sets of unpatched bytes, reaches this state without either record being wrong. Delete whichever is stale to decide it: ${mine.map((backup) => installProvenancePath(backup.path, facts.binaryPath)).join(", ")}. Or reinstall the Claude Code at ${facts.binaryPath} and delete both.`;
7984
+ }
7985
+ function manifestContradictsRecordMessage(facts, manifestChoice, recorded) {
7986
+ return `The patch manifest names ${manifestChoice} as the pristine content of ${facts.binaryPath}, but ${recorded.map((backup) => backup.path).join(", ")} is recorded as that install's pristine content and holds different bytes. Nothing establishes which of them the install at that path is now \u2014 an executable replaced in place with a different build of the same claude version reaches exactly this state \u2014 so clodex will not pick one. Delete whichever of these records is stale to decide it: ${recorded.map((backup) => installProvenancePath(backup.path, facts.binaryPath)).join(", ")}. Or reinstall the Claude Code at ${facts.binaryPath} and delete both: a pristine install needs no backup, and \`clodex patch\` will record its own.`;
7987
+ }
7988
+ function assumedElsewhereMessage(facts, backups) {
7989
+ const others = [...new Set(backups.flatMap((backup) => backup.assumedInstalls))];
7990
+ return `The pristine backup(s) of claude ${facts.version} in ${backupDir()} have already been restored onto ${others.join(", ")} \u2014 not onto ${facts.binaryPath}. That was itself matched on the claude version in a file name, so it is not proof of ownership, but it is a reason not to hand the same bytes to a second install: two installs of one version are different files. Set TWEAKCC_CC_INSTALLATION_PATH=${others[0]} to restore that install, or reinstall Claude Code to make this one pristine.`;
7991
+ }
7863
7992
  function selectRestoreSource(facts) {
7864
7993
  const notes = [];
7994
+ let assumedForThisInstall = false;
7865
7995
  const recorded = facts.manifest;
7866
7996
  const speaksForThisVersion = !recorded?.claudeVersion || recorded.claudeVersion === facts.version;
7867
7997
  const manifest = recorded && recorded.binaryPath === facts.binaryPath && speaksForThisVersion ? recorded : null;
@@ -7873,10 +8003,46 @@ function selectRestoreSource(facts) {
7873
8003
  if (!chosen && manifest && identifiesABackup(manifest)) {
7874
8004
  return { action: "error", message: recordedBackupGoneMessage(facts, manifest) };
7875
8005
  }
8006
+ const manifestDescribesLiveBytes = !!manifest?.patchedSha256 && manifest.patchedSha256 === facts.liveSha256;
8007
+ if (chosen && !manifestDescribesLiveBytes) {
8008
+ const manifestChoice = chosen;
8009
+ const contradicting = facts.backups.filter(
8010
+ (backup) => backup.installs.includes(facts.binaryPath) && backup.sha256 !== manifestChoice.sha256
8011
+ );
8012
+ if (contradicting.length) {
8013
+ return {
8014
+ action: "error",
8015
+ message: manifestContradictsRecordMessage(facts, manifestChoice.path, contradicting)
8016
+ };
8017
+ }
8018
+ }
8019
+ if (!chosen) {
8020
+ const mine = facts.backups.filter((backup) => backup.installs.includes(facts.binaryPath));
8021
+ if (mine.length) {
8022
+ if (new Set(mine.map((backup) => backup.sha256)).size > 1) {
8023
+ return { action: "error", message: contradictoryProvenanceMessage(facts, mine) };
8024
+ }
8025
+ chosen = mine.find((backup) => backup.kind === "content-addressed") ?? mine[0];
8026
+ }
8027
+ }
7876
8028
  if (!chosen) {
7877
8029
  if (other) {
7878
8030
  return facts.backups.length ? { action: "error", message: otherInstallMessage(facts, other.binaryPath) } : { action: "error", message: noBackupMessage(facts) };
7879
8031
  }
8032
+ const recordedElsewhere = facts.backups.filter((backup) => backup.installs.length > 0);
8033
+ if (recordedElsewhere.length) {
8034
+ return { action: "error", message: otherInstallProvenanceMessage(facts, recordedElsewhere) };
8035
+ }
8036
+ const damaged = facts.backups.flatMap((backup) => backup.damagedProvenance);
8037
+ if (damaged.length) {
8038
+ return { action: "error", message: damagedProvenanceMessage(facts, damaged) };
8039
+ }
8040
+ const assumedElsewhere = facts.backups.filter(
8041
+ (backup) => backup.assumedInstalls.some((install) => install !== facts.binaryPath)
8042
+ );
8043
+ if (assumedElsewhere.length) {
8044
+ return { action: "error", message: assumedElsewhereMessage(facts, assumedElsewhere) };
8045
+ }
7880
8046
  const distinct = [...new Set(facts.backups.map((backup) => backup.sha256))];
7881
8047
  if (distinct.length > 1) {
7882
8048
  return {
@@ -7886,12 +8052,14 @@ function selectRestoreSource(facts) {
7886
8052
  }
7887
8053
  chosen = facts.backups.find((backup) => backup.kind === "content-addressed") ?? facts.backups[0];
7888
8054
  if (chosen) {
8055
+ assumedForThisInstall = true;
7889
8056
  notes.push(
7890
- `No patch manifest records ${facts.binaryPath}, so ${chosen.path} is being used as its pristine content on the strength of its claude ${facts.version} version tag alone. On a machine with more than one Claude Code install those bytes may belong to the other one \u2014 a clodex that had recorded this install would have refused rather than guess.`
8057
+ `Neither a patch manifest nor a provenance record ties ${chosen.path} to ${facts.binaryPath}, so it is being used as that install's pristine content on the strength of its claude ${facts.version} version tag alone \u2014 it predates the records clodex now writes. On a machine with more than one Claude Code install those bytes may belong to the other one, and clodex will not record this guess as provenance.`
7891
8058
  );
7892
8059
  }
7893
8060
  }
7894
8061
  if (!chosen) return { action: "error", message: noBackupMessage(facts) };
8062
+ if (manifest?.pristineProvenance === "assumed") assumedForThisInstall = true;
7895
8063
  return {
7896
8064
  action: "restore",
7897
8065
  backupPath: chosen.path,
@@ -7900,6 +8068,7 @@ function selectRestoreSource(facts) {
7900
8068
  // another version's binary, stored under a mislabeled name by an older
7901
8069
  // clodex. Executing it is the only evidence available; require it.
7902
8070
  probeVersion: chosen.kind === "legacy",
8071
+ assumedForThisInstall,
7903
8072
  notes
7904
8073
  };
7905
8074
  }
@@ -8868,16 +9037,64 @@ function responsesWebSocketPartitionKey(wsUrl, payload, options = {}, authorizat
8868
9037
  function inputArray(payload) {
8869
9038
  return Array.isArray(payload.input) ? payload.input : [];
8870
9039
  }
8871
- function normalizeToolCallJson(value) {
8872
- if (Array.isArray(value)) return value.map(normalizeToolCallJson);
9040
+ function toolSchemaDefaults(payload) {
9041
+ const defaults = /* @__PURE__ */ new Map();
9042
+ const add = (tool3) => {
9043
+ if (!tool3 || typeof tool3 !== "object") return;
9044
+ const record = tool3;
9045
+ if (record.type === "namespace" && Array.isArray(record.tools)) {
9046
+ for (const nested of record.tools) add(nested);
9047
+ return;
9048
+ }
9049
+ if (record.type !== "function" || typeof record.name !== "string") return;
9050
+ const parameters = record.parameters;
9051
+ const properties = parameters && typeof parameters === "object" ? parameters.properties : void 0;
9052
+ if (!properties || typeof properties !== "object") return;
9053
+ const perTool = /* @__PURE__ */ new Map();
9054
+ for (const [prop, schema] of Object.entries(properties)) {
9055
+ if (schema && typeof schema === "object" && "default" in schema) {
9056
+ perTool.set(prop, canonicalJson(schema.default));
9057
+ }
9058
+ }
9059
+ if (perTool.size) defaults.set(record.name, perTool);
9060
+ };
9061
+ if (Array.isArray(payload.tools)) for (const tool3 of payload.tools) add(tool3);
9062
+ return defaults;
9063
+ }
9064
+ function toolSchemaDefaultsFingerprint(defaults) {
9065
+ if (!defaults.size) return "none";
9066
+ const tuples = [...defaults.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([name, perTool]) => [
9067
+ name,
9068
+ [...perTool.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
9069
+ ]);
9070
+ return createHash6("sha256").update(JSON.stringify(tuples)).digest("hex").slice(0, 16);
9071
+ }
9072
+ function stripSchemaDefaults(name, args, defaults) {
9073
+ if (!defaults) return args;
9074
+ if (typeof name !== "string" || !args || typeof args !== "object" || Array.isArray(args)) return args;
9075
+ const perTool = defaults.get(name);
9076
+ if (!perTool) return args;
9077
+ const out = {};
9078
+ for (const [key, value] of Object.entries(args)) {
9079
+ const expected = perTool.get(key);
9080
+ if (expected !== void 0 && canonicalJson(value) === expected) continue;
9081
+ out[key] = value;
9082
+ }
9083
+ return out;
9084
+ }
9085
+ function normalizeToolCallJson(value, defaults) {
9086
+ if (Array.isArray(value)) return value.map((item) => normalizeToolCallJson(item, defaults));
8873
9087
  if (!value || typeof value !== "object") return value;
8874
9088
  const record = value;
8875
9089
  const out = {};
8876
- for (const [key, child] of Object.entries(record)) out[key] = normalizeToolCallJson(child);
9090
+ for (const [key, child] of Object.entries(record)) out[key] = normalizeToolCallJson(child, defaults);
8877
9091
  const jsonField = record.type === "function_call" ? "arguments" : record.type === "custom_tool_call" ? "input" : void 0;
8878
9092
  if (jsonField && typeof record[jsonField] === "string") {
8879
9093
  try {
8880
- out[jsonField] = canonicalJson(JSON.parse(record[jsonField]));
9094
+ const parsed = JSON.parse(record[jsonField]);
9095
+ out[jsonField] = canonicalJson(
9096
+ jsonField === "arguments" ? stripSchemaDefaults(record.name, parsed, defaults) : parsed
9097
+ );
8881
9098
  } catch {
8882
9099
  }
8883
9100
  }
@@ -8888,8 +9105,8 @@ function normalizeToolCallJson(value) {
8888
9105
  }
8889
9106
  return out;
8890
9107
  }
8891
- function arraysEqual(left, right) {
8892
- return canonicalJson(normalizeToolCallJson(left)) === canonicalJson(normalizeToolCallJson(right));
9108
+ function arraysEqual(left, right, defaults) {
9109
+ return canonicalJson(normalizeToolCallJson(left, defaults)) === canonicalJson(normalizeToolCallJson(right, defaults));
8893
9110
  }
8894
9111
  function conversationItemKind(value) {
8895
9112
  if (!value || typeof value !== "object") return typeof value;
@@ -8898,17 +9115,17 @@ function conversationItemKind(value) {
8898
9115
  if (typeof record.role === "string") return record.role;
8899
9116
  return "object";
8900
9117
  }
8901
- function conversationItemHash(value) {
8902
- return createHash6("sha256").update(canonicalJson(normalizeToolCallJson(value))).digest("hex").slice(0, 16);
9118
+ function conversationItemHash(value, defaults) {
9119
+ return createHash6("sha256").update(canonicalJson(normalizeToolCallJson(value, defaults))).digest("hex").slice(0, 16);
8903
9120
  }
8904
- function reasoningNormalizationGap(expected, actual) {
9121
+ function reasoningNormalizationGap(expected, actual, defaults) {
8905
9122
  if (conversationItemKind(expected) !== "reasoning" || conversationItemKind(actual) !== "reasoning") return void 0;
8906
9123
  const left = expected;
8907
9124
  const right = actual;
8908
9125
  const blob = left.encrypted_content;
8909
9126
  if (typeof blob !== "string" || !blob || blob !== right.encrypted_content) return void 0;
8910
- const normalizedLeft = normalizeToolCallJson(left);
8911
- const normalizedRight = normalizeToolCallJson(right);
9127
+ const normalizedLeft = normalizeToolCallJson(left, defaults);
9128
+ const normalizedRight = normalizeToolCallJson(right, defaults);
8912
9129
  const fields = [.../* @__PURE__ */ new Set([...Object.keys(normalizedLeft), ...Object.keys(normalizedRight)])].sort().filter((key) => canonicalJson(normalizedLeft[key]) !== canonicalJson(normalizedRight[key]));
8913
9130
  return fields.length ? fields : void 0;
8914
9131
  }
@@ -8956,7 +9173,7 @@ function warnReasoningNormalizationGap(fields, log12) {
8956
9173
  emitParentNotice("clodex: warning: further reasoning-normalization warnings suppressed.");
8957
9174
  }
8958
9175
  }
8959
- function toolArgumentNormalizationGap(expected, actual, requiredProps) {
9176
+ function toolArgumentNormalizationGap(expected, actual, defaults, requiredProps) {
8960
9177
  if (conversationItemKind(expected) !== "function_call") return void 0;
8961
9178
  if (conversationItemKind(actual) !== "function_call") return void 0;
8962
9179
  const left = expected;
@@ -8964,7 +9181,7 @@ function toolArgumentNormalizationGap(expected, actual, requiredProps) {
8964
9181
  const callId = left.call_id;
8965
9182
  if (typeof callId !== "string" || !callId || callId !== right.call_id) return void 0;
8966
9183
  if (typeof left.name !== "string" || left.name !== right.name) return void 0;
8967
- if (canonicalJson(normalizeToolCallJson(left)) === canonicalJson(normalizeToolCallJson(right))) {
9184
+ if (canonicalJson(normalizeToolCallJson(left, defaults)) === canonicalJson(normalizeToolCallJson(right, defaults))) {
8968
9185
  return void 0;
8969
9186
  }
8970
9187
  const required = requiredProps().get(left.name);
@@ -8975,8 +9192,8 @@ function toolArgumentNormalizationGap(expected, actual, requiredProps) {
8975
9192
  const parsed = raw === "" ? {} : JSON.parse(raw);
8976
9193
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
8977
9194
  return canonicalJson({
8978
- ...normalizeToolCallJson(item),
8979
- arguments: canonicalJson(sanitizeToolInput(parsed, required))
9195
+ ...normalizeToolCallJson(item, defaults),
9196
+ arguments: canonicalJson(stripSchemaDefaults(item.name, sanitizeToolInput(parsed, required), defaults))
8980
9197
  });
8981
9198
  } catch {
8982
9199
  return void 0;
@@ -9007,7 +9224,7 @@ function warnToolArgumentNormalizationGap(gap, log12) {
9007
9224
  emitParentNotice("clodex: warning: further tool-argument normalization warnings suppressed.");
9008
9225
  }
9009
9226
  }
9010
- function continuationMismatchDetails(entry, payload, log12, warnOnGap = false, deferWarnings) {
9227
+ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap = false, deferWarnings) {
9011
9228
  const raise = (warn) => {
9012
9229
  if (deferWarnings) deferWarnings.push(warn);
9013
9230
  else warn();
@@ -9017,14 +9234,14 @@ function continuationMismatchDetails(entry, payload, log12, warnOnGap = false, d
9017
9234
  const comparable = Math.min(full.length, prefix.length);
9018
9235
  let mismatch = comparable;
9019
9236
  for (let index = 0; index < comparable; index += 1) {
9020
- if (!arraysEqual([full[index]], [prefix[index]])) {
9237
+ if (!arraysEqual([full[index]], [prefix[index]], defaults)) {
9021
9238
  mismatch = index;
9022
9239
  break;
9023
9240
  }
9024
9241
  }
9025
9242
  const expected = mismatch < prefix.length ? prefix[mismatch] : void 0;
9026
9243
  const actual = mismatch < full.length ? full[mismatch] : void 0;
9027
- const reasoningGap = reasoningNormalizationGap(expected, actual);
9244
+ const reasoningGap = reasoningNormalizationGap(expected, actual, defaults);
9028
9245
  if (reasoningGap && warnOnGap) raise(() => warnReasoningNormalizationGap(reasoningGap, log12));
9029
9246
  let gapExpected = expected;
9030
9247
  if (conversationItemKind(expected) === "reasoning" && conversationItemKind(actual) === "function_call") {
@@ -9040,6 +9257,7 @@ function continuationMismatchDetails(entry, payload, log12, warnOnGap = false, d
9040
9257
  toolArgumentGap = toolArgumentNormalizationGap(
9041
9258
  gapExpected,
9042
9259
  actual,
9260
+ defaults,
9043
9261
  // The head's own schema when it has one; the current turn's tools are only a
9044
9262
  // fallback for a head that predates the snapshot (see headRequiredToolProps).
9045
9263
  () => entry.headRequiredToolProps ?? requiredToolProps(payload)
@@ -9061,8 +9279,8 @@ function continuationMismatchDetails(entry, payload, log12, warnOnGap = false, d
9061
9279
  firstMismatch: mismatch,
9062
9280
  expectedKind: expected === void 0 ? "none" : conversationItemKind(expected),
9063
9281
  actualKind: actual === void 0 ? "none" : conversationItemKind(actual),
9064
- ...expected !== void 0 ? { expectedHash: conversationItemHash(expected) } : {},
9065
- ...actual !== void 0 ? { actualHash: conversationItemHash(actual) } : {},
9282
+ ...expected !== void 0 ? { expectedHash: conversationItemHash(expected, defaults) } : {},
9283
+ ...actual !== void 0 ? { actualHash: conversationItemHash(actual, defaults) } : {},
9066
9284
  ...reasoningGap ? {
9067
9285
  reasoningNormalizationGap: reasoningGap,
9068
9286
  reasoningGapShape: reasoningGapShape(
@@ -9076,8 +9294,8 @@ function continuationMismatchDetails(entry, payload, log12, warnOnGap = false, d
9076
9294
  ...toolArgumentGap ? { toolArgumentNormalizationGap: toolArgumentGap } : {}
9077
9295
  };
9078
9296
  }
9079
- function continuationMismatchSummary(entry, payload, log12, mismatchDump = false, precomputedDetails) {
9080
- const details = precomputedDetails ?? continuationMismatchDetails(entry, payload, log12, true);
9297
+ function continuationMismatchSummary(entry, payload, defaults, log12, mismatchDump = false, precomputedDetails) {
9298
+ const details = precomputedDetails ?? continuationMismatchDetails(entry, payload, defaults, log12, true);
9081
9299
  let summary = `full_items=${details.fullItems} expected_prefix_items=${details.expectedPrefixItems} first_mismatch=${details.firstMismatch} expected=${details.expectedKind} actual=${details.actualKind}`;
9082
9300
  if (details.expectedHash || details.actualHash) {
9083
9301
  summary += ` expected_hash=${details.expectedHash ?? "none"} actual_hash=${details.actualHash ?? "none"}`;
@@ -9085,21 +9303,21 @@ function continuationMismatchSummary(entry, payload, log12, mismatchDump = false
9085
9303
  const full = inputArray(payload);
9086
9304
  const prefix = [...entry.requestInput ?? [], ...entry.expectedAssistant ?? []];
9087
9305
  const index = details.firstMismatch;
9088
- log12(`mismatch dump expected[${index}]: ${mismatchDumpLine(prefix, index)}`);
9089
- log12(`mismatch dump actual[${index}]: ${mismatchDumpLine(full, index)}`);
9306
+ log12(`mismatch dump expected[${index}]: ${mismatchDumpLine(prefix, index, defaults)}`);
9307
+ log12(`mismatch dump actual[${index}]: ${mismatchDumpLine(full, index, defaults)}`);
9090
9308
  }
9091
9309
  }
9092
9310
  return summary;
9093
9311
  }
9094
- function mismatchDumpLine(items, index) {
9312
+ function mismatchDumpLine(items, index, defaults) {
9095
9313
  if (index >= items.length) return "(absent)";
9096
- const line = canonicalJson(normalizeToolCallJson(items[index]));
9314
+ const line = canonicalJson(normalizeToolCallJson(items[index], defaults));
9097
9315
  const max = 2e3;
9098
9316
  const marker = " [truncated]";
9099
9317
  return line.length <= max ? line : line.slice(0, max - marker.length) + marker;
9100
9318
  }
9101
- function canonicalItemStrings(items) {
9102
- return items.map((item) => canonicalJson(normalizeToolCallJson([item])));
9319
+ function canonicalItemStrings(items, defaults) {
9320
+ return items.map((item) => canonicalJson(normalizeToolCallJson([item], defaults)));
9103
9321
  }
9104
9322
  function isPrefixOrEqual(head, client) {
9105
9323
  if (client.length < head.length) return false;
@@ -9111,16 +9329,24 @@ function isPrefixOrEqual(head, client) {
9111
9329
  function isStrictPrefix(head, client) {
9112
9330
  return client.length > head.length && isPrefixOrEqual(head, client);
9113
9331
  }
9114
- function continuationMatch(entry, payload, clientItems) {
9332
+ function continuationMatch(entry, payload, clientItems, defaults, defaultsId) {
9115
9333
  if (!entry.responseId || !entry.requestInput || !entry.expectedAssistant) return void 0;
9116
9334
  const full = inputArray(payload);
9117
- entry.canonicalPrefix ??= canonicalItemStrings([...entry.requestInput, ...entry.expectedAssistant]);
9335
+ if (entry.canonicalToolDefaultsId !== defaultsId) {
9336
+ entry.canonicalPrefix = void 0;
9337
+ entry.canonicalEchoablePrefix = void 0;
9338
+ entry.canonicalToolDefaultsId = defaultsId;
9339
+ }
9340
+ entry.canonicalPrefix ??= canonicalItemStrings(
9341
+ [...entry.requestInput, ...entry.expectedAssistant],
9342
+ defaults
9343
+ );
9118
9344
  if (isStrictPrefix(entry.canonicalPrefix, clientItems)) {
9119
9345
  return { delta: full.slice(entry.canonicalPrefix.length), mode: "exact" };
9120
9346
  }
9121
9347
  const echoedAssistant = entry.expectedAssistant.filter((item) => conversationItemKind(item) !== "reasoning");
9122
9348
  if (echoedAssistant.length === entry.expectedAssistant.length) return void 0;
9123
- entry.canonicalEchoablePrefix ??= canonicalItemStrings([...entry.requestInput, ...echoedAssistant]);
9349
+ entry.canonicalEchoablePrefix ??= canonicalItemStrings([...entry.requestInput, ...echoedAssistant], defaults);
9124
9350
  if (!isStrictPrefix(entry.canonicalEchoablePrefix, clientItems)) return void 0;
9125
9351
  return { delta: full.slice(entry.canonicalEchoablePrefix.length), mode: "omitted_reasoning" };
9126
9352
  }
@@ -10029,13 +10255,15 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10029
10255
  authorizationFingerprint,
10030
10256
  claudeAgentId
10031
10257
  );
10258
+ const requestToolDefaults = toolSchemaDefaults(payload);
10259
+ const requestToolDefaultsId = toolSchemaDefaultsFingerprint(requestToolDefaults);
10032
10260
  const promptFingerprint = responsesWebSocketPromptFingerprint(payload);
10033
10261
  const promptFieldHashes = responsesWebSocketPromptFieldHashes(payload);
10034
10262
  const instructionsSnapshot = instructionsFromPayload(payload);
10035
10263
  let now = resolvedOptions.now();
10036
10264
  const evictions = cleanupExpiredConnections(now);
10037
10265
  let canonicalClientItems;
10038
- const clientItems = () => canonicalClientItems ??= canonicalItemStrings(inputArray(payload));
10266
+ const clientItems = () => canonicalClientItems ??= canonicalItemStrings(inputArray(payload), requestToolDefaults);
10039
10267
  const scanForHeads = () => {
10040
10268
  const scanned = partitionKey ? connectionEntries(partitionKey) : [];
10041
10269
  const idle = scanned.filter((entry) => !entry.inFlight);
@@ -10043,16 +10271,32 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10043
10271
  return {
10044
10272
  candidates: scanned,
10045
10273
  idleCandidates: idle,
10046
- matches: idle.map((entry) => ({ entry, match: continuationMatch(entry, payload, canonical) })).filter((candidate) => candidate.match !== void 0).sort((left, right) => left.match.delta.length - right.match.delta.length || (left.match.mode === right.match.mode ? 0 : left.match.mode === "exact" ? -1 : 1))
10274
+ matches: idle.map((entry) => ({
10275
+ entry,
10276
+ match: continuationMatch(entry, payload, canonical, requestToolDefaults, requestToolDefaultsId)
10277
+ })).filter((candidate) => candidate.match !== void 0).sort((left, right) => left.match.delta.length - right.match.delta.length || (left.match.mode === right.match.mode ? 0 : left.match.mode === "exact" ? -1 : 1))
10047
10278
  };
10048
10279
  };
10049
10280
  const couldPrecedeThisRequest = (entry) => {
10050
10281
  if (entry.responseId && entry.requestInput && entry.expectedAssistant) {
10051
- return continuationMatch(entry, payload, clientItems()) !== void 0;
10282
+ return continuationMatch(
10283
+ entry,
10284
+ payload,
10285
+ clientItems(),
10286
+ requestToolDefaults,
10287
+ requestToolDefaultsId
10288
+ ) !== void 0;
10052
10289
  }
10053
10290
  const streaming = entry.current;
10054
10291
  if (!streaming) return true;
10055
- streaming.canonicalInput ??= canonicalItemStrings(inputArray(streaming.originalPayload));
10292
+ if (streaming.canonicalInputToolDefaultsId !== requestToolDefaultsId) {
10293
+ streaming.canonicalInput = void 0;
10294
+ streaming.canonicalInputToolDefaultsId = requestToolDefaultsId;
10295
+ }
10296
+ streaming.canonicalInput ??= canonicalItemStrings(
10297
+ inputArray(streaming.originalPayload),
10298
+ requestToolDefaults
10299
+ );
10056
10300
  return isPrefixOrEqual(streaming.canonicalInput, clientItems());
10057
10301
  };
10058
10302
  const blockingHead = (entries) => entries.find((entry) => entry.inFlight && couldPrecedeThisRequest(entry));
@@ -10110,6 +10354,7 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10110
10354
  const diagnosticMismatch = continuationMismatchDetails(
10111
10355
  diagnosticEntry,
10112
10356
  payload,
10357
+ requestToolDefaults,
10113
10358
  debug,
10114
10359
  true,
10115
10360
  deferredMismatchWarnings
@@ -10119,13 +10364,21 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10119
10364
  if (candidate === diagnosticEntry) continue;
10120
10365
  candidateMismatchDetails.set(
10121
10366
  candidate,
10122
- continuationMismatchDetails(candidate, payload, debug, true, deferredMismatchWarnings)
10367
+ continuationMismatchDetails(
10368
+ candidate,
10369
+ payload,
10370
+ requestToolDefaults,
10371
+ debug,
10372
+ true,
10373
+ deferredMismatchWarnings
10374
+ )
10123
10375
  );
10124
10376
  }
10125
10377
  debug(
10126
10378
  `history mismatch starting an additional chain; retained ${candidates.length} existing head(s) (${continuationMismatchSummary(
10127
10379
  diagnosticEntry,
10128
10380
  payload,
10381
+ requestToolDefaults,
10129
10382
  debug,
10130
10383
  mismatchDump,
10131
10384
  diagnosticMismatch
@@ -10249,7 +10502,7 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10249
10502
  input: {
10250
10503
  count: requestInput.length,
10251
10504
  kinds: requestInput.map(conversationItemKind),
10252
- hashes: requestInput.map(conversationItemHash)
10505
+ hashes: requestInput.map((item) => conversationItemHash(item, requestToolDefaults))
10253
10506
  },
10254
10507
  candidateCount: candidates.length,
10255
10508
  idleCandidateCount: idleCandidates.length,
@@ -10282,7 +10535,7 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10282
10535
  ttlPausedMs: entry.ttlPausedMs,
10283
10536
  idleMs: Math.max(0, now - entry.lastUsedAt),
10284
10537
  promptChanges: changedPromptFields(entry.promptFieldHashes, promptFieldHashes),
10285
- mismatch: candidateMismatchDetails?.get(entry) ?? continuationMismatchDetails(entry, payload, debug)
10538
+ mismatch: candidateMismatchDetails?.get(entry) ?? continuationMismatchDetails(entry, payload, requestToolDefaults, debug)
10286
10539
  })),
10287
10540
  evictions
10288
10541
  }, diagnosticCorrelation);
@@ -10362,7 +10615,7 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
10362
10615
  }
10363
10616
 
10364
10617
  // src/oauth/claude-identity.ts
10365
- import { createHash as createHash7, randomUUID as randomUUID2 } from "crypto";
10618
+ import { createHash as createHash7, randomUUID as randomUUID3 } from "crypto";
10366
10619
  var CLAUDE_CODE_CLI_VERSION = "2.1.195";
10367
10620
  var CLAUDE_CODE_USER_AGENT = `claude-cli/${CLAUDE_CODE_CLI_VERSION} (external, cli)`;
10368
10621
  var CLAUDE_CODE_ENTRYPOINT = process.env.CLAUDE_CODE_ENTRYPOINT ?? "cli";
@@ -10371,7 +10624,7 @@ var sessionCache = /* @__PURE__ */ new Map();
10371
10624
  function getOrCreateSessionId(seed) {
10372
10625
  let id = sessionCache.get(seed);
10373
10626
  if (!id) {
10374
- id = randomUUID2();
10627
+ id = randomUUID3();
10375
10628
  sessionCache.set(seed, id);
10376
10629
  }
10377
10630
  return id;
@@ -11719,7 +11972,7 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
11719
11972
  }
11720
11973
 
11721
11974
  // src/proxy.ts
11722
- import { randomUUID as randomUUID3 } from "crypto";
11975
+ import { randomUUID as randomUUID4 } from "crypto";
11723
11976
 
11724
11977
  // src/sdk-adapter.ts
11725
11978
  import { createHash as createHash8 } from "crypto";
@@ -13145,7 +13398,7 @@ function configuredAliasLookupNames(alias) {
13145
13398
  }))].map(normalizeRouteLookupId);
13146
13399
  }
13147
13400
  async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenceLogPath, debugLogPath, webSocketDiagnosticsLogPath, modelAliases) {
13148
- const proxyToken = randomUUID3();
13401
+ const proxyToken = randomUUID4();
13149
13402
  silenceSdkWarnings();
13150
13403
  if (routes.length === 0) {
13151
13404
  throw new Error("Proxy catalog requires at least one route");
@@ -13309,6 +13562,14 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
13309
13562
  const isOAuth = routeAuthType === "oauth";
13310
13563
  let effectiveBeta = inboundBeta;
13311
13564
  let claudeCodeSessionId;
13565
+ const inboundSessionRaw = req.headers["x-claude-code-session-id"];
13566
+ const goSessionHeaders = openCodeGoSessionHeaders(
13567
+ { providerId: route.providerId, baseUrl: upstreamUrl },
13568
+ extractClaudeSessionId(
13569
+ anthropicBody,
13570
+ Array.isArray(inboundSessionRaw) ? inboundSessionRaw[0] : inboundSessionRaw
13571
+ )
13572
+ );
13312
13573
  if (isOAuth) {
13313
13574
  const seed = route.providerId ?? route.realModelId;
13314
13575
  const identity = injectClaudeIdentity(forwardBody, route.providerData, seed);
@@ -13326,7 +13587,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
13326
13587
  authType: routeAuthType,
13327
13588
  log: (message) => plog(message),
13328
13589
  claudeCodeSessionId,
13329
- extraHeaders: route.headers,
13590
+ extraHeaders: { ...route.headers, ...goSessionHeaders },
13330
13591
  refreshToken: route.refreshToken,
13331
13592
  onTokenRefreshed: (refreshed) => {
13332
13593
  route.apiKey = refreshed;
@@ -13386,6 +13647,11 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
13386
13647
  upstreamModelId: route.realModelId
13387
13648
  }
13388
13649
  });
13650
+ const goSdkSessionHeaders = openCodeGoSessionHeaders(
13651
+ { providerId: route.providerId, baseUrl: route.baseURL },
13652
+ claudeSessionId
13653
+ );
13654
+ if (goSdkSessionHeaders) params.headers = { ...params.headers, ...goSdkSessionHeaders };
13389
13655
  plog(
13390
13656
  () => `sdk: npm=${route.npm} model=${route.realModelId}, stream=${clientWantsStream}, tools=${anthropicBody.tools?.length ?? 0}, msgs=${params.messages.length}`
13391
13657
  );
@@ -13545,7 +13811,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
13545
13811
  res,
13546
13812
  upstreamStatus === 500 ? 502 : upstreamStatus,
13547
13813
  clientMessage,
13548
- contextLengthExceeded ? relayRequestId ?? randomUUID3() : void 0
13814
+ contextLengthExceeded ? relayRequestId ?? randomUUID4() : void 0
13549
13815
  );
13550
13816
  } else {
13551
13817
  const errorType = anthropicErrorType(upstreamStatus, details?.transportCode);
@@ -13553,7 +13819,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
13553
13819
  data: ${JSON.stringify({
13554
13820
  type: "error",
13555
13821
  error: { type: errorType, message: clientMessage },
13556
- ...contextLengthExceeded ? { request_id: relayRequestId ?? randomUUID3() } : {}
13822
+ ...contextLengthExceeded ? { request_id: relayRequestId ?? randomUUID4() } : {}
13557
13823
  })}
13558
13824
 
13559
13825
  `);
@@ -13800,7 +14066,7 @@ function readPatchManifest(path = getPatchManifestPath()) {
13800
14066
  }
13801
14067
  function writePatchManifest(manifest, path = getPatchManifestPath()) {
13802
14068
  mkdirSync5(getAppHome(), { recursive: true, mode: 448 });
13803
- writeFileSync5(path, `${JSON.stringify(manifest, null, 2)}
14069
+ writeFileSync6(path, `${JSON.stringify(manifest, null, 2)}
13804
14070
  `, { encoding: "utf8", mode: 384 });
13805
14071
  }
13806
14072
  function buildPatchModelConfig(favorites, aliases, modelMetaFor) {
@@ -13958,7 +14224,7 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
13958
14224
  try {
13959
14225
  const fd = openSync7(lockPath, "wx");
13960
14226
  const content = { pid: process.pid, startedAt: now };
13961
- writeFileSync5(fd, JSON.stringify(content));
14227
+ writeFileSync6(fd, JSON.stringify(content));
13962
14228
  closeSync7(fd);
13963
14229
  return () => {
13964
14230
  try {
@@ -13983,10 +14249,19 @@ function tryAcquirePatchLock(lockPath = getPatchLockPath(), opts = {}) {
13983
14249
  }
13984
14250
  return null;
13985
14251
  }
14252
+ var ignoredLaunchOverride = null;
14253
+ function takeIgnoredLaunchOverride() {
14254
+ const ignored = ignoredLaunchOverride;
14255
+ ignoredLaunchOverride = null;
14256
+ return ignored;
14257
+ }
13986
14258
  function resolveClaudeBinaryForPatch() {
13987
- const envOverride = process.env["TWEAKCC_CC_INSTALLATION_PATH"];
14259
+ const envOverride = process.env["TWEAKCC_CC_INSTALLATION_PATH"]?.trim() || null;
13988
14260
  const nativeSymlink = join8(homedir3(), ".local", "bin", "claude");
13989
- const source = envOverride?.trim() || (existsSync5(nativeSymlink) ? nativeSymlink : null) || findClaudeBinary();
14261
+ if (envOverride && !existsSync5(envOverride)) {
14262
+ return { ok: false, reason: "patch-target-missing", declaredPath: envOverride };
14263
+ }
14264
+ const source = envOverride || (existsSync5(nativeSymlink) ? nativeSymlink : null) || findClaudeBinary();
13990
14265
  if (!source) return { ok: false, reason: "binary-not-found" };
13991
14266
  let resolved;
13992
14267
  try {
@@ -14006,6 +14281,17 @@ function resolveClaudeBinaryForPatch() {
14006
14281
  };
14007
14282
  }
14008
14283
  resolved = followed.path;
14284
+ const launchOverride = process.env["CLODEX_CLAUDE_PATH"]?.trim() || null;
14285
+ let launchOverrideResolved = null;
14286
+ if (launchOverride) {
14287
+ try {
14288
+ const followedOverride = resolveThroughNpmShims(realpathSync2(launchOverride));
14289
+ launchOverrideResolved = followedOverride.ok ? followedOverride.path : realpathSync2(launchOverride);
14290
+ } catch {
14291
+ launchOverrideResolved = launchOverride;
14292
+ }
14293
+ }
14294
+ ignoredLaunchOverride = launchOverrideResolved && launchOverrideResolved !== resolved ? { used: resolved, ignored: launchOverride } : null;
14009
14295
  try {
14010
14296
  if (!statSync8(resolved).isFile()) return { ok: false, reason: "binary-not-found" };
14011
14297
  } catch {
@@ -14029,7 +14315,7 @@ function installScriptCommand(path) {
14029
14315
  }
14030
14316
  function describePatchTargetFailure(target, command = "patch") {
14031
14317
  if (target.reason === "launcher-unresolved") {
14032
- return `${target.shimPath} starts Claude Code but is not Claude Code itself, and clodex could not follow it: ${target.detail}. clodex will not patch a launcher script \u2014 that fails with "Unable to detect installation type". Set CLODEX_CLAUDE_PATH to the Claude Code program itself (for an npm install on Windows that is node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe under the directory holding the launcher), then run the command again.`;
14318
+ return `${target.shimPath} starts Claude Code but is not Claude Code itself, and clodex could not follow it: ${target.detail}. clodex will not patch a launcher script \u2014 that fails with "Unable to detect installation type". Set TWEAKCC_CC_INSTALLATION_PATH to the Claude Code program itself (for an npm install on Windows that is node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe under the directory holding the launcher), then run the command again.`;
14033
14319
  }
14034
14320
  if (target.reason === "native-binary-missing") {
14035
14321
  const installer = installScriptCommand(target.installScriptPath);
@@ -14038,6 +14324,9 @@ function describePatchTargetFailure(target, command = "patch") {
14038
14324
  const restore = command === "restore" ? ` If you need to restore by hand, pristine backups are in ${backupDir()}.` : "";
14039
14325
  return `${target.binaryPath} is Claude Code's npm placeholder, not its native binary, so the npm install is incomplete. ${remedy} ${retry}${restore} If this is a custom wrapper rather than the npm placeholder, set TWEAKCC_CC_INSTALLATION_PATH to the native Claude Code binary.`;
14040
14326
  }
14327
+ if (target.reason === "patch-target-missing") {
14328
+ return `TWEAKCC_CC_INSTALLATION_PATH is set to ${target.declaredPath}, which does not exist. clodex will not look for another Claude Code instead \u2014 patching or restoring a different install than the one you named is how one install's pristine bytes end up over another's. Point it at the Claude Code program, or unset it to let clodex find your install.`;
14329
+ }
14041
14330
  return target.reason === "binary-not-found" ? "claude binary not found. Install Claude Code or set TWEAKCC_CC_INSTALLATION_PATH." : `Could not determine the version of ${target.binaryPath} (\`claude --version\` failed). clodex will not patch a binary whose version it cannot read, because the version selects the pristine backup it patches from. If a previous patch left the install broken, \`clodex patch --restore\` still works \u2014 it reads the version from the patch manifest.`;
14042
14331
  }
14043
14332
  function summarizePatchResults(results) {
@@ -14060,14 +14349,15 @@ function verifyPristineSource(plan, version) {
14060
14349
  message: `Refusing to use ${plan.backupPath} as the pristine source for claude ${version}: it reports ${backupVersion ? `version ${backupVersion}` : "no version at all"}. That backup predates content-addressed backup names and does not hold this version's bytes. Remove it (or reinstall Claude Code), then run \`clodex patch\`.`
14061
14350
  };
14062
14351
  }
14063
- function publishBackupFile(from, to) {
14352
+ function publishFileByRename(from, to, mode) {
14064
14353
  const temp = `${to}.tmp-${process.pid}-${Date.now().toString(36)}`;
14065
14354
  try {
14066
14355
  copyFileSync(from, temp);
14067
- renameSync2(temp, to);
14356
+ if (mode !== void 0) chmodSync4(temp, mode);
14357
+ renameSync3(temp, to);
14068
14358
  } catch (err) {
14069
14359
  try {
14070
- rmSync(temp, { force: true });
14360
+ rmSync2(temp, { force: true });
14071
14361
  } catch {
14072
14362
  }
14073
14363
  throw err;
@@ -14131,6 +14421,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14131
14421
  let patchedSha256;
14132
14422
  let backup;
14133
14423
  let pristineSha256;
14424
+ const pristine = { provenance: "established" };
14134
14425
  try {
14135
14426
  mkdirSync5(backupDir(), { recursive: true });
14136
14427
  const { tryDetectInstallation, readContent, writeContent } = await import("tweakcc");
@@ -14179,6 +14470,18 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14179
14470
  }
14180
14471
  pristineSha256 = plan.pristineSha256;
14181
14472
  backup = plan.backupPath;
14473
+ const inheritsAGuess = (path) => {
14474
+ const existing = readInstallProvenance(installProvenancePath(path, binaryPath));
14475
+ return existing === "damaged" || existing !== null && existing.assumed;
14476
+ };
14477
+ const provenanceAssumed = plan.action === "restore" && plan.assumedForThisInstall || inheritsAGuess(plan.backupPath) || inheritsAGuess(contentAddressedBackupPath(version, plan.pristineSha256)) || inheritsAGuess(legacyBackupPath(version)) || facts.backups.some(
14478
+ (candidate) => candidate.sha256 === plan.pristineSha256 && candidate.assumedInstalls.includes(binaryPath)
14479
+ );
14480
+ const recordProvenance = (path) => {
14481
+ if (recordBackupProvenance(path, binaryPath, { assumed: provenanceAssumed }) === "assumed") {
14482
+ pristine.provenance = "assumed";
14483
+ }
14484
+ };
14182
14485
  if (!loaded) {
14183
14486
  loaded = await seedCandidate(backup);
14184
14487
  if (isPatchedClaudeSource(loaded.source)) {
@@ -14190,17 +14493,22 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14190
14493
  `the patch candidate no longer matches the pristine bytes it was seeded from; refusing to publish it as ${plan.backupPath}`
14191
14494
  );
14192
14495
  }
14193
- publishBackupFile(candidatePath, plan.backupPath);
14496
+ recordProvenance(plan.backupPath);
14497
+ publishFileByRename(candidatePath, plan.backupPath);
14194
14498
  }
14195
14499
  const canonical = contentAddressedBackupPath(version, pristineSha256);
14196
14500
  if (canonical !== backup) {
14501
+ recordProvenance(backup);
14502
+ recordProvenance(canonical);
14197
14503
  const alreadyStored = facts.backups.some(
14198
14504
  (candidate) => candidate.path === canonical && candidate.sha256 === pristineSha256
14199
14505
  );
14200
- if (!alreadyStored) publishBackupFile(backup, canonical);
14506
+ if (!alreadyStored) publishFileByRename(backup, canonical);
14201
14507
  backup = canonical;
14202
14508
  }
14203
- publishBackupFile(backup, tweakccMirrorBackupPath());
14509
+ publishFileByRename(backup, tweakccMirrorBackupPath());
14510
+ recordProvenance(backup);
14511
+ if (plan.backupPath !== backup) recordProvenance(plan.backupPath);
14204
14512
  const builtIn = applyClodexPatches(loaded.source, desired.config);
14205
14513
  results = builtIn.results;
14206
14514
  const failedEffortPatches = requiredEffortPatchFailures(results);
@@ -14267,7 +14575,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14267
14575
  else if (publishedBlob) resignMachOBinary(candidatePath);
14268
14576
  patchedSize = statSync8(candidatePath).size;
14269
14577
  patchedSha256 = sha256File(candidatePath);
14270
- renameSync2(candidatePath, binaryPath);
14578
+ renameSync3(candidatePath, binaryPath);
14271
14579
  } catch (err) {
14272
14580
  const detailLines = err instanceof PatchApplyError ? summarizePatchResults(err.results) : [];
14273
14581
  if (opts.trace && detailLines.length) {
@@ -14282,7 +14590,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14282
14590
  } finally {
14283
14591
  if (candidateDir !== void 0) {
14284
14592
  try {
14285
- rmSync(candidateDir, { recursive: true, force: true });
14593
+ rmSync2(candidateDir, { recursive: true, force: true });
14286
14594
  } catch (err) {
14287
14595
  if (opts.trace) {
14288
14596
  process.stderr.write(
@@ -14305,6 +14613,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14305
14613
  patchedSha256,
14306
14614
  backupPath: backup,
14307
14615
  pristineSha256,
14616
+ ...pristine.provenance === "assumed" ? { pristineProvenance: "assumed" } : {},
14308
14617
  patchedAt: (/* @__PURE__ */ new Date()).toISOString()
14309
14618
  };
14310
14619
  writePatchManifest(manifest);
@@ -14318,7 +14627,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
14318
14627
  };
14319
14628
  }
14320
14629
  function runRestoreCommand(target) {
14321
- if (!target.ok && (target.reason === "binary-not-found" || target.reason === "native-binary-missing")) {
14630
+ if (!target.ok && (target.reason === "binary-not-found" || target.reason === "patch-target-missing" || target.reason === "native-binary-missing")) {
14322
14631
  p2.log.error(describePatchTargetFailure(target, "restore"));
14323
14632
  return 1;
14324
14633
  }
@@ -14353,16 +14662,65 @@ function runRestoreCommand(target) {
14353
14662
  p2.log.error(verified.message);
14354
14663
  return 1;
14355
14664
  }
14356
- copyFileSync(plan.backupPath, binaryPath);
14665
+ if (manifest && manifest.backupPath && manifest.binaryPath === binaryPath && manifest.claudeVersion !== version && manifest.backupPath !== plan.backupPath) {
14666
+ try {
14667
+ if (existsSync5(manifest.backupPath)) {
14668
+ recordBackupProvenance(manifest.backupPath, manifest.binaryPath, {
14669
+ assumed: manifest.pristineProvenance === "assumed"
14670
+ });
14671
+ }
14672
+ } catch (err) {
14673
+ p2.log.warn(
14674
+ `Could not record in ${backupDir()} that ${manifest.backupPath} holds the pristine bytes of claude ${manifest.claudeVersion} at ${manifest.binaryPath} (${err instanceof Error ? err.message : String(err)}). That version may need to be reinstalled rather than restored.`
14675
+ );
14676
+ }
14677
+ }
14678
+ let recorded;
14357
14679
  try {
14358
- unlinkSync3(getPatchManifestPath());
14680
+ recorded = recordBackupProvenance(plan.backupPath, binaryPath, { assumed: plan.assumedForThisInstall });
14681
+ } catch (err) {
14682
+ recorded = "failed";
14683
+ const detail = err instanceof Error ? err.message : String(err);
14684
+ if (plan.assumedForThisInstall) {
14685
+ p2.log.error(
14686
+ `Refusing to restore ${binaryPath} from ${plan.backupPath}: nothing but the claude ${version} version tag ties those bytes to this install, and clodex cannot record that in ${backupDir()} (${detail}). Writing them without that record would leave the machine unable to tell afterwards that the match was a guess. Fix the backup directory, or reinstall Claude Code to make this install pristine.`
14687
+ );
14688
+ return 1;
14689
+ }
14690
+ p2.log.warn(
14691
+ `Could not record in ${backupDir()} that ${plan.backupPath} holds the pristine bytes of ${binaryPath} (${detail}). Restoring anyway and keeping the patch manifest, so that record is not lost \u2014 a later restore would otherwise have nothing tying that backup to this install.`
14692
+ );
14693
+ }
14694
+ let publishedMode;
14695
+ try {
14696
+ publishedMode = statSync8(binaryPath).mode;
14359
14697
  } catch {
14360
14698
  }
14699
+ try {
14700
+ publishFileByRename(plan.backupPath, binaryPath, publishedMode);
14701
+ } catch (err) {
14702
+ p2.log.warn(
14703
+ `Could not replace ${binaryPath} through a new file (${err instanceof Error ? err.message : String(err)}); writing the pristine bytes in place instead. If claude then fails to start with a code-signing error, copy the backup to a new file and move it over the binary by hand.`
14704
+ );
14705
+ copyFileSync(plan.backupPath, binaryPath);
14706
+ }
14707
+ if (recorded === "established" && (!manifest || manifest.binaryPath === binaryPath)) {
14708
+ try {
14709
+ unlinkSync3(getPatchManifestPath());
14710
+ } catch {
14711
+ }
14712
+ }
14361
14713
  p2.log.success(`Restored pristine claude ${version} from ${plan.backupPath}.`);
14362
14714
  return 0;
14363
14715
  }
14364
14716
  async function runPatchCommand(opts = {}) {
14365
14717
  const target = resolveClaudeBinaryForPatch();
14718
+ const ignoredOverride = takeIgnoredLaunchOverride();
14719
+ if (ignoredOverride) {
14720
+ p2.log.warn(
14721
+ `CLODEX_CLAUDE_PATH is set to ${ignoredOverride.ignored}, but it does not choose what gets patched \u2014 ${ignoredOverride.used} does. CLODEX_CLAUDE_PATH selects the claude that gets LAUNCHED; set TWEAKCC_CC_INSTALLATION_PATH to patch a specific install.`
14722
+ );
14723
+ }
14366
14724
  if (opts.restore) return runRestoreCommand(target);
14367
14725
  if (!target.ok) {
14368
14726
  p2.log.error(describePatchTargetFailure(target));
@@ -14405,6 +14763,19 @@ async function runPatchCommand(opts = {}) {
14405
14763
  p2.log.warn("Another clodex process is patching the claude binary right now \u2014 skipped.");
14406
14764
  return 1;
14407
14765
  }
14766
+ if (manifest && manifest.backupPath && (manifest.binaryPath !== binaryPath || manifest.claudeVersion !== version)) {
14767
+ try {
14768
+ if (existsSync5(manifest.backupPath)) {
14769
+ recordBackupProvenance(manifest.backupPath, manifest.binaryPath, {
14770
+ assumed: manifest.pristineProvenance === "assumed"
14771
+ });
14772
+ }
14773
+ } catch (err) {
14774
+ p2.log.warn(
14775
+ `Could not record in ${backupDir()} that ${manifest.backupPath} holds the pristine bytes of ${manifest.binaryPath} before replacing the patch manifest (${err instanceof Error ? err.message : String(err)}). That install may need to be reinstalled rather than restored.`
14776
+ );
14777
+ }
14778
+ }
14408
14779
  try {
14409
14780
  const outcome = await applyPatch(binaryPath, version, desired, configHash, {
14410
14781
  trace: opts.trace ?? false,
@@ -14784,7 +15155,7 @@ function localProvidersToServerModels(localProviders) {
14784
15155
  }
14785
15156
 
14786
15157
  // src/registry/credential-cleanup-journal.ts
14787
- import { randomUUID as randomUUID4 } from "crypto";
15158
+ import { randomUUID as randomUUID5 } from "crypto";
14788
15159
  import {
14789
15160
  closeSync as closeSync8,
14790
15161
  existsSync as existsSync6,
@@ -14794,9 +15165,9 @@ import {
14794
15165
  mkdirSync as mkdirSync6,
14795
15166
  openSync as openSync8,
14796
15167
  readFileSync as readFileSync11,
14797
- renameSync as renameSync3,
15168
+ renameSync as renameSync4,
14798
15169
  unlinkSync as unlinkSync4,
14799
- writeFileSync as writeFileSync6
15170
+ writeFileSync as writeFileSync7
14800
15171
  } from "fs";
14801
15172
  import { dirname as dirname6 } from "path";
14802
15173
  var JOURNAL_SCHEMA_VERSION = 1;
@@ -14924,17 +15295,17 @@ function writeJournalUnlocked(journal, path) {
14924
15295
  assertRegistryWriteOwnership(path);
14925
15296
  ensureSecureAppHome();
14926
15297
  mkdirSync6(dirname6(path), { recursive: true, mode: DIR_MODE2 });
14927
- const tmp = `${path}.${process.pid}.${randomUUID4()}.tmp`;
15298
+ const tmp = `${path}.${process.pid}.${randomUUID5()}.tmp`;
14928
15299
  let fd;
14929
15300
  try {
14930
15301
  fd = openSync8(tmp, "wx", FILE_MODE4);
14931
- writeFileSync6(fd, `${JSON.stringify(journal, null, 2)}
15302
+ writeFileSync7(fd, `${JSON.stringify(journal, null, 2)}
14932
15303
  `);
14933
15304
  fsyncSync2(fd);
14934
15305
  closeSync8(fd);
14935
15306
  fd = void 0;
14936
15307
  assertRegistryWriteOwnership(path);
14937
- renameSync3(tmp, path);
15308
+ renameSync4(tmp, path);
14938
15309
  syncParentDirectory(path);
14939
15310
  } finally {
14940
15311
  if (fd !== void 0) closeSync8(fd);
@@ -17817,7 +18188,7 @@ async function askSaveServerPassword() {
17817
18188
 
17818
18189
  // src/server/router.ts
17819
18190
  import { createServer as createServer2 } from "http";
17820
- import { randomUUID as randomUUID5 } from "crypto";
18191
+ import { randomUUID as randomUUID6 } from "crypto";
17821
18192
 
17822
18193
  // src/openai-adapter.ts
17823
18194
  import { tool as tool2, jsonSchema as jsonSchema2, streamText as streamText2, generateText as generateText2 } from "ai";
@@ -18233,7 +18604,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
18233
18604
  plog(`model not found: ${body.model}`);
18234
18605
  return;
18235
18606
  }
18236
- const requestId = randomUUID5();
18607
+ const requestId = randomUUID6();
18237
18608
  const claudeSessionIdHeader = Array.isArray(req.headers["x-claude-code-session-id"]) ? req.headers["x-claude-code-session-id"][0] : req.headers["x-claude-code-session-id"];
18238
18609
  const claudeSessionId = extractClaudeSessionId(body, claudeSessionIdHeader);
18239
18610
  const claudeAgentIds = extractClaudeAgentIds(req.headers);
@@ -18273,6 +18644,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
18273
18644
  const forwardBody = { ...body, model: upstreamModelId(model) };
18274
18645
  const authType = model.authType ?? "api";
18275
18646
  const isOAuth = authType === "oauth";
18647
+ const goSessionHeaders = openCodeGoSessionHeaders(model, claudeSessionId);
18276
18648
  auditInference(options, {
18277
18649
  requestId,
18278
18650
  modelId: body.model,
@@ -18303,7 +18675,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
18303
18675
  authType,
18304
18676
  log: (message) => plog(message),
18305
18677
  claudeCodeSessionId,
18306
- extraHeaders: model.headers,
18678
+ extraHeaders: { ...model.headers, ...goSessionHeaders },
18307
18679
  refreshToken,
18308
18680
  onTokenRefreshed: (refreshed) => {
18309
18681
  model.apiKey = refreshed;
@@ -18375,6 +18747,8 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
18375
18747
  maxTools: npmMaxTools,
18376
18748
  log: plog
18377
18749
  });
18750
+ const goSdkSessionHeaders = openCodeGoSessionHeaders(model, claudeSessionId);
18751
+ if (goSdkSessionHeaders) params.headers = { ...params.headers, ...goSdkSessionHeaders };
18378
18752
  const clientWantsStream = Boolean(body.stream);
18379
18753
  const responseModelId = getResponseModelId(body.model, model, options);
18380
18754
  plog(() => `sdk npm=${model.npm} upstream=${upstreamModelId(model)} responseModel=${responseModelId} stream=${clientWantsStream}`);
@@ -18566,6 +18940,8 @@ async function handleOpenAIChatCompletions(req, res, options, modelCache, plog)
18566
18940
  }
18567
18941
  const model = lookupModel(res, options.catalog, body.model);
18568
18942
  if (!model) return;
18943
+ const openAiSessionIdHeader = Array.isArray(req.headers["x-claude-code-session-id"]) ? req.headers["x-claude-code-session-id"][0] : req.headers["x-claude-code-session-id"];
18944
+ const goSessionHeaders = openCodeGoSessionHeaders(model, openAiSessionIdHeader);
18569
18945
  if (supportsDirectOpenAIChatCompletions(model)) {
18570
18946
  if (model.completionsUrl && !/^https?:\/\//i.test(model.completionsUrl)) {
18571
18947
  sendJson(res, 400, { error: { message: `Invalid provider completionsUrl: must be http:// or https://` } });
@@ -18602,7 +18978,7 @@ async function handleOpenAIChatCompletions(req, res, options, modelCache, plog)
18602
18978
  try {
18603
18979
  await relayAnthropicMessages(res, completionsUrl, forwardBody, apiKey2, Boolean(body.stream), {
18604
18980
  authType: model.authType ?? "api",
18605
- extraHeaders: model.headers,
18981
+ extraHeaders: { ...model.headers, ...goSessionHeaders },
18606
18982
  refreshToken,
18607
18983
  onTokenRefreshed: (refreshed) => {
18608
18984
  model.apiKey = refreshed;
@@ -18647,6 +19023,7 @@ async function handleOpenAIChatCompletions(req, res, options, modelCache, plog)
18647
19023
  const baseURL = model.modelFormat === "anthropic" ? model.baseUrl : model.apiBaseUrl;
18648
19024
  const openAiOAuth = isOpenAiOAuthRoute(model);
18649
19025
  const params = translateOpenAiRequest(body, { openAiOAuth });
19026
+ if (goSessionHeaders) params.headers = { ...params.headers, ...goSessionHeaders };
18650
19027
  const clientWantsStream = Boolean(body.stream);
18651
19028
  const responseModelId = getResponseModelId(body.model, model, options);
18652
19029
  plog(() => `sdk-openai npm=${npm} upstream=${upstreamModelId(model)} responseModel=${responseModelId} stream=${clientWantsStream}`);
@@ -18901,13 +19278,13 @@ import * as http2 from "http";
18901
19278
  import * as https from "https";
18902
19279
  import * as net from "net";
18903
19280
  import { PassThrough } from "stream";
18904
- import { randomUUID as randomUUID6 } from "crypto";
19281
+ import { randomUUID as randomUUID7 } from "crypto";
18905
19282
  import { URL as URL2 } from "url";
18906
19283
  import { createBrotliDecompress, createGunzip, createInflate } from "zlib";
18907
19284
 
18908
19285
  // src/http-proxy/ca.ts
18909
19286
  import { randomBytes as randomBytes2 } from "crypto";
18910
- import { chmodSync as chmodSync4, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
19287
+ import { chmodSync as chmodSync5, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "fs";
18911
19288
  import { dirname as dirname7, join as join9, resolve as resolve3 } from "path";
18912
19289
  import forge from "node-forge";
18913
19290
  var CERT_DIR = "http-proxy";
@@ -18934,16 +19311,16 @@ function certPaths() {
18934
19311
  };
18935
19312
  }
18936
19313
  function writePrivate(path, value) {
18937
- writeFileSync7(path, value, { encoding: "utf8", mode: 384 });
18938
- chmodSync4(path, 384);
19314
+ writeFileSync8(path, value, { encoding: "utf8", mode: 384 });
19315
+ chmodSync5(path, 384);
18939
19316
  }
18940
19317
  function writePublic(path, value) {
18941
- writeFileSync7(path, value, { encoding: "utf8", mode: 420 });
18942
- chmodSync4(path, 420);
19318
+ writeFileSync8(path, value, { encoding: "utf8", mode: 420 });
19319
+ chmodSync5(path, 420);
18943
19320
  }
18944
19321
  function generateCertificates(paths) {
18945
19322
  mkdirSync7(paths.dir, { recursive: true, mode: 448 });
18946
- chmodSync4(paths.dir, 448);
19323
+ chmodSync5(paths.dir, 448);
18947
19324
  const caKeys = forge.pki.rsa.generateKeyPair(2048);
18948
19325
  const caCert = forge.pki.createCertificate();
18949
19326
  caCert.publicKey = caKeys.publicKey;
@@ -19759,7 +20136,7 @@ async function startHttpProxy(options) {
19759
20136
  }
19760
20137
  const messagesEndpoint = anthropicMessagesEndpoint(req.url);
19761
20138
  if (req.method === "POST" && messagesEndpoint) {
19762
- const requestId = randomUUID6();
20139
+ const requestId = randomUUID7();
19763
20140
  let parsed = null;
19764
20141
  let route;
19765
20142
  let adapterBody = rawBody;