@khalilgharbaoui/opencode-claude-code-plugin 0.48.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/claude-code-language-model.ts
2
- import { join as join12 } from "path";
2
+ import { join as join13 } from "path";
3
3
 
4
4
  // src/ids.ts
5
5
  import { randomInt } from "crypto";
@@ -991,7 +991,10 @@ function formatUnattendedReplayNote(dropped) {
991
991
  var UNRECOGNIZED_MODEL_STDERR_MARKER = "[claude-code:unrecognized_model]";
992
992
  var MODEL_CLI_FLOORS = {
993
993
  // "Added Claude Sonnet 5.5 (`claude-sonnet-5-5`)" (CHANGELOG, 2.1.284).
994
- "claude-sonnet-5-5": "2.1.284"
994
+ "claude-sonnet-5-5": "2.1.284",
995
+ // "Added Claude Haiku 5.5 (`claude-haiku-5-5`), now the default Haiku model
996
+ // on the Anthropic API" (CHANGELOG, 2.1.293). Absent from 2.1.288's catalog.
997
+ "claude-haiku-5-5": "2.1.293"
995
998
  };
996
999
  function parseUnrecognizedModel(stderr) {
997
1000
  const at = stderr.indexOf(UNRECOGNIZED_MODEL_STDERR_MARKER);
@@ -1885,8 +1888,8 @@ function cliSupportsInteractiveBypass(v) {
1885
1888
  if (!v) return false;
1886
1889
  return gte(v, { major: 2, minor: 1, patch: 263 });
1887
1890
  }
1888
- var INTERACTIVE_MEASURED_CLI = "2.1.288";
1889
- var INTERACTIVE_MEASURED = { major: 2, minor: 1, patch: 288 };
1891
+ var INTERACTIVE_MEASURED_CLI = "2.1.293";
1892
+ var INTERACTIVE_MEASURED = { major: 2, minor: 1, patch: 293 };
1890
1893
  function isUnmeasuredInteractiveCli(v) {
1891
1894
  if (!v) return false;
1892
1895
  return gte(v, INTERACTIVE_MEASURED) && !(v.major === INTERACTIVE_MEASURED.major && v.minor === INTERACTIVE_MEASURED.minor && v.patch === INTERACTIVE_MEASURED.patch);
@@ -3511,33 +3514,97 @@ function clearCompression(sessionKey2) {
3511
3514
  }
3512
3515
 
3513
3516
  // src/mcp-bridge.ts
3514
- import * as fs4 from "fs";
3515
- import * as path5 from "path";
3516
- import * as os2 from "os";
3517
+ import * as fs5 from "fs";
3518
+ import * as path6 from "path";
3519
+ import * as os3 from "os";
3517
3520
  import * as crypto2 from "crypto";
3518
3521
  import {
3519
3522
  parse as parseJsonc,
3520
3523
  printParseErrorCode
3521
3524
  } from "jsonc-parser";
3525
+
3526
+ // src/mcp-auth.ts
3527
+ import * as fs4 from "fs";
3528
+ import * as os2 from "os";
3529
+ import * as path5 from "path";
3530
+ var EXPIRY_SKEW_SECONDS = 30;
3531
+ function opencodeStateDir() {
3532
+ const dataHome = process.env.XDG_DATA_HOME || path5.join(os2.homedir(), ".local", "share");
3533
+ return path5.join(dataHome, "opencode");
3534
+ }
3535
+ function readMcpAuthStore(stateDir) {
3536
+ const file = path5.join(stateDir, "mcp-auth.json");
3537
+ try {
3538
+ const stat = fs4.lstatSync(file);
3539
+ if (!stat.isFile()) {
3540
+ log.debug("ignoring opencode MCP auth store that is not a regular file", { path: file });
3541
+ return {};
3542
+ }
3543
+ if (typeof stat.uid === "number" && typeof process.getuid === "function" && stat.uid !== process.getuid()) {
3544
+ log.debug("ignoring opencode MCP auth store owned by another user", { path: file });
3545
+ return {};
3546
+ }
3547
+ const parsed = JSON.parse(fs4.readFileSync(file, "utf8"));
3548
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
3549
+ log.debug("opencode MCP auth store has an unexpected shape", { path: file });
3550
+ return {};
3551
+ }
3552
+ return parsed;
3553
+ } catch {
3554
+ return {};
3555
+ }
3556
+ }
3557
+ function selectBearerToken(store, serverName, serverUrl, now = Date.now()) {
3558
+ const entry = Object.prototype.hasOwnProperty.call(store, serverName) ? store[serverName] : void 0;
3559
+ if (!entry || typeof entry !== "object") return { token: null, refusal: "no-entry" };
3560
+ if (!entry.serverUrl || entry.serverUrl !== serverUrl) {
3561
+ return { token: null, refusal: "url-mismatch" };
3562
+ }
3563
+ const accessToken = entry.tokens?.accessToken;
3564
+ if (typeof accessToken !== "string" || accessToken.length === 0) {
3565
+ return { token: null, refusal: "no-token" };
3566
+ }
3567
+ const expiresAt = entry.tokens?.expiresAt;
3568
+ if (typeof expiresAt === "number" && expiresAt - now / 1e3 < EXPIRY_SKEW_SECONDS) {
3569
+ return { token: null, refusal: "expired" };
3570
+ }
3571
+ return { token: accessToken, expiresAt: typeof expiresAt === "number" ? expiresAt : void 0 };
3572
+ }
3573
+ function buildFreshnessKey(expiresAt) {
3574
+ return expiresAt === void 0 ? "no-expiry" : String(expiresAt);
3575
+ }
3576
+ function hasAuthorizationHeader(headers) {
3577
+ return Object.keys(headers).some((key) => key.toLowerCase() === "authorization");
3578
+ }
3579
+ function injectBearerHeaders(server2, accessToken) {
3580
+ const existing = server2.headers && typeof server2.headers === "object" && !Array.isArray(server2.headers) ? server2.headers : {};
3581
+ if (hasAuthorizationHeader(existing)) return { ...server2 };
3582
+ return {
3583
+ ...server2,
3584
+ headers: { ...existing, Authorization: `Bearer ${accessToken}` }
3585
+ };
3586
+ }
3587
+
3588
+ // src/mcp-bridge.ts
3522
3589
  var FILE_NAMES = ["opencode.jsonc", "opencode.json", "config.json"];
3523
3590
  var PROJECT_FILE_NAMES = ["opencode.json", "opencode.jsonc"];
3524
3591
  function fileExists(p) {
3525
3592
  try {
3526
- return fs4.statSync(p).isFile();
3593
+ return fs5.statSync(p).isFile();
3527
3594
  } catch {
3528
3595
  return false;
3529
3596
  }
3530
3597
  }
3531
3598
  function dirExists(p) {
3532
3599
  try {
3533
- return fs4.statSync(p).isDirectory();
3600
+ return fs5.statSync(p).isDirectory();
3534
3601
  } catch {
3535
3602
  return false;
3536
3603
  }
3537
3604
  }
3538
3605
  function readAndParse(file) {
3539
3606
  try {
3540
- const raw = fs4.readFileSync(file, "utf8");
3607
+ const raw = fs5.readFileSync(file, "utf8");
3541
3608
  const errors = [];
3542
3609
  const parsed = parseJsonc(raw, errors, { allowTrailingComma: true });
3543
3610
  if (errors.length > 0) {
@@ -3573,14 +3640,14 @@ function deepMerge(target, source) {
3573
3640
  }
3574
3641
  function walkUp(opts) {
3575
3642
  const out = [];
3576
- let current = path5.resolve(opts.start);
3643
+ let current = path6.resolve(opts.start);
3577
3644
  while (true) {
3578
3645
  for (const target of opts.targets) {
3579
- const candidate = path5.join(current, target);
3646
+ const candidate = path6.join(current, target);
3580
3647
  if (opts.predicate(candidate)) out.push(candidate);
3581
3648
  }
3582
- if (opts.stop && current === path5.resolve(opts.stop)) break;
3583
- const parent = path5.dirname(current);
3649
+ if (opts.stop && current === path6.resolve(opts.stop)) break;
3650
+ const parent = path6.dirname(current);
3584
3651
  if (parent === current) break;
3585
3652
  current = parent;
3586
3653
  }
@@ -3588,28 +3655,28 @@ function walkUp(opts) {
3588
3655
  }
3589
3656
  function detectWorktree(cwd) {
3590
3657
  const override = process.env.OPENCODE_WORKTREE;
3591
- if (override) return path5.resolve(override);
3592
- let current = path5.resolve(cwd);
3658
+ if (override) return path6.resolve(override);
3659
+ let current = path6.resolve(cwd);
3593
3660
  while (true) {
3594
- const gitPath = path5.join(current, ".git");
3661
+ const gitPath = path6.join(current, ".git");
3595
3662
  try {
3596
- if (fs4.existsSync(gitPath)) return current;
3663
+ if (fs5.existsSync(gitPath)) return current;
3597
3664
  } catch {
3598
3665
  }
3599
- const parent = path5.dirname(current);
3666
+ const parent = path6.dirname(current);
3600
3667
  if (parent === current) return void 0;
3601
3668
  current = parent;
3602
3669
  }
3603
3670
  }
3604
3671
  function globalConfigDir() {
3605
- const xdg = process.env.XDG_CONFIG_HOME ?? path5.join(os2.homedir(), ".config");
3606
- return path5.join(xdg, "opencode");
3672
+ const xdg = process.env.XDG_CONFIG_HOME ?? path6.join(os3.homedir(), ".config");
3673
+ return path6.join(xdg, "opencode");
3607
3674
  }
3608
3675
  function loadGlobalConfig(hostApi = "v1") {
3609
3676
  const dir = globalConfigDir();
3610
3677
  let merged = {};
3611
3678
  for (const name of FILE_NAMES.slice().reverse()) {
3612
- const file = path5.join(dir, name);
3679
+ const file = path6.join(dir, name);
3613
3680
  if (!fileExists(file)) continue;
3614
3681
  const parsed = readAndParse(file);
3615
3682
  if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
@@ -3619,7 +3686,7 @@ function loadGlobalConfig(hostApi = "v1") {
3619
3686
  function loadProjectFilesInDir(dir, hostApi = "v1") {
3620
3687
  let merged = {};
3621
3688
  for (const name of PROJECT_FILE_NAMES) {
3622
- const file = path5.join(dir, name);
3689
+ const file = path6.join(dir, name);
3623
3690
  if (!fileExists(file)) continue;
3624
3691
  const parsed = readAndParse(file);
3625
3692
  if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
@@ -3630,7 +3697,7 @@ function dotOpencodeDirs(cwd, worktree) {
3630
3697
  const dirs = [];
3631
3698
  const seen = /* @__PURE__ */ new Set();
3632
3699
  const push = (p) => {
3633
- const abs = path5.resolve(p);
3700
+ const abs = path6.resolve(p);
3634
3701
  if (!seen.has(abs) && dirExists(abs)) {
3635
3702
  seen.add(abs);
3636
3703
  dirs.push(abs);
@@ -3644,9 +3711,9 @@ function dotOpencodeDirs(cwd, worktree) {
3644
3711
  })) {
3645
3712
  push(dir);
3646
3713
  }
3647
- const home = os2.homedir();
3714
+ const home = os3.homedir();
3648
3715
  if (home) {
3649
- const homeDot = path5.join(home, ".opencode");
3716
+ const homeDot = path6.join(home, ".opencode");
3650
3717
  if (dirExists(homeDot)) push(homeDot);
3651
3718
  }
3652
3719
  const envDir = process.env.OPENCODE_CONFIG_DIR;
@@ -3664,7 +3731,33 @@ function substituteEnvPlaceholders(source) {
3664
3731
  }
3665
3732
  return out;
3666
3733
  }
3667
- function translateServer(name, spec) {
3734
+ var warnedExpiredBearer = /* @__PURE__ */ new Set();
3735
+ function withBearerToken(name, spec, url, auth) {
3736
+ if (!auth) return spec;
3737
+ const lookup = selectBearerToken(auth.store, name, url);
3738
+ if (lookup.token === null) {
3739
+ if (lookup.refusal === "expired" && !warnedExpiredBearer.has(name)) {
3740
+ warnedExpiredBearer.add(name);
3741
+ log.warn(
3742
+ "opencode's stored OAuth token for this MCP server has expired, so the bridged server carries no credential; re-authenticate it in opencode",
3743
+ { server: name }
3744
+ );
3745
+ } else {
3746
+ log.debug("no opencode OAuth token for bridged MCP server", {
3747
+ server: name,
3748
+ reason: lookup.refusal
3749
+ });
3750
+ }
3751
+ return spec;
3752
+ }
3753
+ log.info("bridged MCP server carries opencode's stored OAuth token", { server: name });
3754
+ return injectBearerHeaders(spec, lookup.token);
3755
+ }
3756
+ function bearerAuthContext(options) {
3757
+ if (!options?.oauthTokens) return void 0;
3758
+ return { store: readMcpAuthStore(options.stateDir ?? opencodeStateDir()) };
3759
+ }
3760
+ function translateServer(name, spec, auth) {
3668
3761
  if (spec.enabled === false) return null;
3669
3762
  const type = spec.type;
3670
3763
  if (type === "local") {
@@ -3690,7 +3783,7 @@ function translateServer(name, spec) {
3690
3783
  log.warn("skipping remote MCP server with no url", { name });
3691
3784
  return null;
3692
3785
  }
3693
- const out = {
3786
+ let out = {
3694
3787
  type: "http",
3695
3788
  url: spec.url
3696
3789
  };
@@ -3699,7 +3792,7 @@ function translateServer(name, spec) {
3699
3792
  spec.headers
3700
3793
  );
3701
3794
  }
3702
- return out;
3795
+ return withBearerToken(name, out, spec.url, auth);
3703
3796
  }
3704
3797
  log.warn("skipping MCP server with unknown type", {
3705
3798
  name,
@@ -3750,18 +3843,19 @@ function mergeMcp(target, source) {
3750
3843
  return out;
3751
3844
  }
3752
3845
  var MCP_PENDING_STATUS = "pending";
3753
- function bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, hostApi = "v1") {
3846
+ function bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, hostApi = "v1", auth) {
3754
3847
  const {
3755
3848
  servers: merged,
3756
3849
  enabledServerNames: allEnabledServerNames,
3757
3850
  hash
3758
- } = mergeOpencodeMcp(cwd, runtimeStatus, hostApi);
3851
+ } = mergeOpencodeMcp(cwd, runtimeStatus, hostApi, auth);
3852
+ const authContext = bearerAuthContext(auth);
3759
3853
  const servers = {};
3760
3854
  const bridgedServerNames = [];
3761
3855
  for (const [name, spec] of Object.entries(merged)) {
3762
3856
  if (!spec || typeof spec !== "object") continue;
3763
3857
  if (excludeServers?.has(name)) continue;
3764
- const translated = translateServer(name, spec);
3858
+ const translated = translateServer(name, spec, authContext);
3765
3859
  if (translated) {
3766
3860
  servers[name] = translated;
3767
3861
  bridgedServerNames.push(name);
@@ -3793,7 +3887,7 @@ function opencodeConfigLayers(cwd, hostApi = "v1") {
3793
3887
  const projectDirs = [];
3794
3888
  const seenProjectDirs = /* @__PURE__ */ new Set();
3795
3889
  for (const f of projectFiles) {
3796
- const d = path5.dirname(f);
3890
+ const d = path6.dirname(f);
3797
3891
  if (!seenProjectDirs.has(d)) {
3798
3892
  seenProjectDirs.add(d);
3799
3893
  projectDirs.push(d);
@@ -3819,7 +3913,7 @@ function loadMergedOpencodeConfig(cwd, hostApi = "v1") {
3819
3913
  for (const layer of opencodeConfigLayers(cwd, hostApi)) merged = mergeConfigLayer(merged, layer, hostApi);
3820
3914
  return merged;
3821
3915
  }
3822
- function mergeOpencodeMcp(cwd, runtimeStatus, hostApi = "v1") {
3916
+ function mergeOpencodeMcp(cwd, runtimeStatus, hostApi = "v1", auth) {
3823
3917
  let merged = {};
3824
3918
  for (const layer of opencodeConfigLayers(cwd, hostApi)) {
3825
3919
  const servers = extractMcpBlock(layer);
@@ -3843,7 +3937,19 @@ function mergeOpencodeMcp(cwd, runtimeStatus, hostApi = "v1") {
3843
3937
  enabledServerNames.push(name);
3844
3938
  }
3845
3939
  const mergedBody = JSON.stringify({ mcpServers: merged }, null, 2);
3846
- const hash = crypto2.createHash("sha256").update(mergedBody).digest("hex").slice(0, 12);
3940
+ const hasher = crypto2.createHash("sha256").update(mergedBody);
3941
+ const authContext = bearerAuthContext(auth);
3942
+ if (authContext) {
3943
+ for (const [name, spec] of Object.entries(merged)) {
3944
+ if (!spec || typeof spec !== "object") continue;
3945
+ const server2 = spec;
3946
+ if (server2.type !== "remote" || typeof server2.url !== "string") continue;
3947
+ const lookup = selectBearerToken(authContext.store, name, server2.url);
3948
+ if (lookup.token === null) continue;
3949
+ hasher.update(`\0bearer:${name}:${buildFreshnessKey(lookup.expiresAt)}`);
3950
+ }
3951
+ }
3952
+ const hash = hasher.digest("hex").slice(0, 12);
3847
3953
  return { servers: merged, enabledServerNames, hash };
3848
3954
  }
3849
3955
  function finishBridge(input) {
@@ -3860,13 +3966,13 @@ function finishBridge(input) {
3860
3966
  }
3861
3967
  const body = JSON.stringify({ mcpServers: servers }, null, 2);
3862
3968
  const bodyDigest = crypto2.createHash("sha256").update(body).digest("hex").slice(0, 12);
3863
- const outPath = path5.join(
3969
+ const outPath = path6.join(
3864
3970
  pluginTmpDir(),
3865
3971
  `mcp-${bodyDigest}.json`
3866
3972
  );
3867
3973
  try {
3868
3974
  if (!fileExists(outPath)) {
3869
- fs4.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
3975
+ fs5.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
3870
3976
  }
3871
3977
  } catch (e) {
3872
3978
  log.warn("failed to write bridged MCP config", {
@@ -4533,17 +4639,17 @@ function tailIsReplyOnly(history, from) {
4533
4639
  }
4534
4640
 
4535
4641
  // src/session-resume-store.ts
4536
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
4537
- import { homedir as homedir4 } from "os";
4538
- import { join as join5 } from "path";
4642
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
4643
+ import { homedir as homedir5 } from "os";
4644
+ import { join as join6 } from "path";
4539
4645
  var MAX_RESUME_RECORDS = 256;
4540
4646
  var RESUME_RECORD_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
4541
4647
  var records = null;
4542
4648
  var pathOverride = null;
4543
4649
  function resumeStorePath() {
4544
4650
  if (pathOverride) return pathOverride;
4545
- const stateRoot = process.env.XDG_STATE_HOME || join5(homedir4(), ".local", "state");
4546
- return join5(stateRoot, "opencode-claude-code-plugin", "claude-sessions.json");
4651
+ const stateRoot = process.env.XDG_STATE_HOME || join6(homedir5(), ".local", "state");
4652
+ return join6(stateRoot, "opencode-claude-code-plugin", "claude-sessions.json");
4547
4653
  }
4548
4654
  function isDigest(value) {
4549
4655
  const entry = value;
@@ -4557,7 +4663,7 @@ function load() {
4557
4663
  if (records) return records;
4558
4664
  records = /* @__PURE__ */ new Map();
4559
4665
  try {
4560
- const parsed = JSON.parse(readFileSync2(resumeStorePath(), "utf8"));
4666
+ const parsed = JSON.parse(readFileSync3(resumeStorePath(), "utf8"));
4561
4667
  const entries = parsed && typeof parsed === "object" ? parsed.sessions : null;
4562
4668
  if (entries && typeof entries === "object") {
4563
4669
  const cutoff = Date.now() - RESUME_RECORD_MAX_AGE_MS;
@@ -4572,7 +4678,7 @@ function save(update) {
4572
4678
  const file = resumeStorePath();
4573
4679
  const merged = /* @__PURE__ */ new Map();
4574
4680
  try {
4575
- const onDisk = JSON.parse(readFileSync2(file, "utf8"))?.sessions;
4681
+ const onDisk = JSON.parse(readFileSync3(file, "utf8"))?.sessions;
4576
4682
  if (onDisk && typeof onDisk === "object") {
4577
4683
  for (const [key, record] of Object.entries(onDisk)) {
4578
4684
  if (isRecord2(record)) merged.set(key, record);
@@ -4585,7 +4691,7 @@ function save(update) {
4585
4691
  const kept = [...merged.entries()].filter(([, record]) => record.updatedAt >= cutoff).sort((a, b) => a[1].updatedAt - b[1].updatedAt).slice(-MAX_RESUME_RECORDS);
4586
4692
  records = new Map(kept);
4587
4693
  try {
4588
- mkdirSync3(join5(file, ".."), { recursive: true, mode: 448 });
4694
+ mkdirSync3(join6(file, ".."), { recursive: true, mode: 448 });
4589
4695
  const tmp = `${file}.${process.pid}.tmp`;
4590
4696
  writeFileSync3(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {
4591
4697
  mode: 384
@@ -6034,7 +6140,7 @@ async function handleBtwCommand(client, input, options = {}) {
6034
6140
  // src/diagnostic-bundle.ts
6035
6141
  import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
6036
6142
  import { openSync, readSync, closeSync, fstatSync, realpathSync } from "fs";
6037
- import { homedir as homedir5 } from "os";
6143
+ import { homedir as homedir6 } from "os";
6038
6144
 
6039
6145
  // src/log-messages.ts
6040
6146
  var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
@@ -6130,6 +6236,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
6130
6236
  "no proxyTools entry was recognised; nothing will be proxied this turn",
6131
6237
  "no stdout after envelope write; respawning claude process to resume conversation",
6132
6238
  "opencode 2 event subscription ended; deleted sessions keep their claude process until idle eviction",
6239
+ "opencode's stored OAuth token for this MCP server has expired, so the bridged server carries no credential; re-authenticate it in opencode",
6133
6240
  "pending proxy call arrived after stream close; rejecting",
6134
6241
  'permissionMode "plan" is enforced: claude cannot edit files or run commands, and --dangerously-skip-permissions is deliberately not passed so it stays that way. Headless Claude Code is not offered an ExitPlanMode tool, so nothing releases plan mode mid-session; approving a plan in chat does not unlock writes. Leaving plan mode means changing the config and restarting opencode.',
6135
6242
  `permissionPreset "read-only": this claude CLI has no --permission-prompts (2.1.263+), so permission requests are denied by the plugin's own can_use_tool handler instead of by the CLI. Same outcome, one layer fewer.`,
@@ -6395,7 +6502,7 @@ function createIdHasher(salt = randomBytes2(16).toString("hex")) {
6395
6502
  return (raw) => createHash4("sha256").update(salt).update("\0").update(raw).digest("hex").slice(0, 8);
6396
6503
  }
6397
6504
  function createRedactionContext(options = {}) {
6398
- const home = options.home ?? homedir5();
6505
+ const home = options.home ?? homedir6();
6399
6506
  const homes = /* @__PURE__ */ new Set();
6400
6507
  if (home) homes.add(home);
6401
6508
  if (home) {
@@ -6580,8 +6687,8 @@ function redactLogLine(raw, context) {
6580
6687
  if (!parsed.data || Object.keys(parsed.data).length === 0) return head;
6581
6688
  return `${head} ${JSON.stringify(sanitizeLogData(parsed.data, context))}`;
6582
6689
  }
6583
- function readLogTail(path14, maxBytes = BUNDLE_TAIL_BYTES) {
6584
- const fd = openSync(path14, "r");
6690
+ function readLogTail(path15, maxBytes = BUNDLE_TAIL_BYTES) {
6691
+ const fd = openSync(path15, "r");
6585
6692
  try {
6586
6693
  const size = fstatSync(fd).size;
6587
6694
  const length = Math.min(size, maxBytes);
@@ -6786,14 +6893,14 @@ async function fetchPlanUsage(cliPath, options = {}) {
6786
6893
  }
6787
6894
 
6788
6895
  // src/stale-build.ts
6789
- import * as fs6 from "fs";
6790
- import * as path7 from "path";
6896
+ import * as fs7 from "fs";
6897
+ import * as path8 from "path";
6791
6898
  import { fileURLToPath as fileURLToPath2 } from "url";
6792
6899
 
6793
6900
  // src/startup-diagnostics.ts
6794
6901
  import { execFile as execFile3 } from "child_process";
6795
- import * as fs5 from "fs";
6796
- import * as path6 from "path";
6902
+ import * as fs6 from "fs";
6903
+ import * as path7 from "path";
6797
6904
  import { promisify as promisify2 } from "util";
6798
6905
  import { fileURLToPath } from "url";
6799
6906
 
@@ -6828,8 +6935,8 @@ var cachedPluginVersion;
6828
6935
  function pluginVersion() {
6829
6936
  if (cachedPluginVersion) return cachedPluginVersion;
6830
6937
  try {
6831
- const here = path6.dirname(fileURLToPath(import.meta.url));
6832
- const raw = fs5.readFileSync(path6.join(here, "..", "package.json"), "utf8");
6938
+ const here = path7.dirname(fileURLToPath(import.meta.url));
6939
+ const raw = fs6.readFileSync(path7.join(here, "..", "package.json"), "utf8");
6833
6940
  const version = JSON.parse(raw).version;
6834
6941
  cachedPluginVersion = typeof version === "string" ? version : "unknown";
6835
6942
  } catch {
@@ -6853,7 +6960,7 @@ var opencodeVersionProbe;
6853
6960
  function detectOpencodeVersion(execPath = process.execPath) {
6854
6961
  if (opencodeVersionProbe) return opencodeVersionProbe;
6855
6962
  opencodeVersionProbe = (async () => {
6856
- if (!path6.basename(execPath).toLowerCase().includes("opencode")) {
6963
+ if (!path7.basename(execPath).toLowerCase().includes("opencode")) {
6857
6964
  log.debug("skipping opencode version probe: execPath is not opencode", { execPath });
6858
6965
  return void 0;
6859
6966
  }
@@ -7082,10 +7189,10 @@ function defaultEntryPath() {
7082
7189
  }
7083
7190
  function readOnDiskBuild(entryPath) {
7084
7191
  try {
7085
- const raw = fs6.readFileSync(path7.join(path7.dirname(entryPath), "..", "package.json"), "utf8");
7192
+ const raw = fs7.readFileSync(path8.join(path8.dirname(entryPath), "..", "package.json"), "utf8");
7086
7193
  const version = JSON.parse(raw).version;
7087
7194
  if (typeof version !== "string" || version.length === 0) return void 0;
7088
- const stat = fs6.statSync(entryPath);
7195
+ const stat = fs7.statSync(entryPath);
7089
7196
  return { version, mtimeMs: stat.mtimeMs, size: stat.size };
7090
7197
  } catch {
7091
7198
  return void 0;
@@ -7096,7 +7203,7 @@ function recordLoadedBuild() {
7096
7203
  let mtimeMs;
7097
7204
  let size;
7098
7205
  try {
7099
- const stat = fs6.statSync(entryPath);
7206
+ const stat = fs7.statSync(entryPath);
7100
7207
  mtimeMs = stat.mtimeMs;
7101
7208
  size = stat.size;
7102
7209
  } catch {
@@ -7534,7 +7641,7 @@ Could not build the report: ${message}`;
7534
7641
 
7535
7642
  // src/agent-models.ts
7536
7643
  import { readFile, readdir } from "fs/promises";
7537
- import path8 from "path";
7644
+ import path9 from "path";
7538
7645
 
7539
7646
  // src/models.ts
7540
7647
  var PROVIDER_ID = "claude-code";
@@ -7593,6 +7700,8 @@ function passthroughModel(id) {
7593
7700
  };
7594
7701
  }
7595
7702
  var haikuCost = { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 };
7703
+ var haiku55Cost = { input: 0.1, output: 0.5, cacheRead: 0.01, cacheWrite: 0.125 };
7704
+ var haiku55LongPromptCost = { input: 0.5, output: 2.5, cacheRead: 0.05, cacheWrite: 0.625 };
7596
7705
  var sonnetCost = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
7597
7706
  var sonnet5Cost = { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 };
7598
7707
  var opusCost = { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 };
@@ -7633,6 +7742,9 @@ function toConfigModel(model) {
7633
7742
  variants: model.variants
7634
7743
  };
7635
7744
  }
7745
+ var LONG_PROMPT_COSTS = {
7746
+ "claude-haiku-5-5": { aboveTokens: 1e5, cost: haiku55LongPromptCost }
7747
+ };
7636
7748
  var defaultModels = {
7637
7749
  "claude-haiku-4-5": defineModel({
7638
7750
  id: "claude-haiku-4-5",
@@ -7645,6 +7757,32 @@ var defaultModels = {
7645
7757
  multiplier: 1,
7646
7758
  releaseDate: "2025-10-01"
7647
7759
  }),
7760
+ // Haiku 5.5 needs Claude Code 2.1.293+, the release that added it and made
7761
+ // it the default Haiku; an older CLI runs it on fallback limits and says so
7762
+ // on stderr, which `reportUnrecognizedModel` turns into a warning. Unlike
7763
+ // Haiku 4.5 it reasons: the CLI's catalog gives it `effort`, `max_effort`,
7764
+ // `xhigh_effort` and `adaptive_thinking`, and Anthropic's migration note says
7765
+ // manual `budget_tokens` is a 400 and adaptive thinking is on by default. The
7766
+ // plugin never sends `budget_tokens`, so nothing here special-cases it, the
7767
+ // same as Opus 5.5's `rejects_disabled_thinking`. No `-fast` entry: the
7768
+ // catalog gives it no `fast_mode` capability.
7769
+ //
7770
+ // The multiplier is 0.1 because it is exactly a tenth of Haiku 4.5 on input,
7771
+ // output, cache read and cache write alike, and Haiku 4.5's $1/$5 is the 1x
7772
+ // anchor every other entry here is already measured against. Re-anchoring the
7773
+ // whole table on Haiku 5.5 would change every model's displayed number for no
7774
+ // gain in accuracy.
7775
+ "claude-haiku-5-5": defineModel({
7776
+ id: "claude-haiku-5-5",
7777
+ name: "Claude Haiku 5.5",
7778
+ family: "haiku",
7779
+ reasoning: true,
7780
+ context: 1e6,
7781
+ output: 128e3,
7782
+ cost: haiku55Cost,
7783
+ multiplier: 0.1,
7784
+ releaseDate: "2026-10-07"
7785
+ }),
7648
7786
  "claude-sonnet-4-5": defineModel({
7649
7787
  id: "claude-sonnet-4-5",
7650
7788
  name: "Claude Sonnet 4.5",
@@ -7873,14 +8011,25 @@ function apiCallCostUsd(model, usage) {
7873
8011
  const base = model.replace(/@.*$/, "").replace(/\[1m\]$/i, "").replace(/-\d{8}$/, "");
7874
8012
  const entry = (usage.speed === "fast" ? defaultModels[`${base}${FAST_SUFFIX}`] : void 0) ?? defaultModels[base];
7875
8013
  if (!entry) return null;
7876
- const cost = entry.cost;
7877
8014
  const count = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
8015
+ const cost = longPromptCost(base, usage, count) ?? entry.cost;
7878
8016
  const split = usage.cache_creation;
7879
8017
  const write1h = split ? count(split.ephemeral_1h_input_tokens) : 0;
7880
8018
  const write5m = split ? count(split.ephemeral_5m_input_tokens) : count(usage.cache_creation_input_tokens);
7881
8019
  const perMillion = count(usage.input_tokens) * cost.input + count(usage.output_tokens) * cost.output + count(usage.cache_read_input_tokens) * cost.cache.read + write5m * cost.cache.write + write1h * cost.input * 2;
7882
8020
  return perMillion / 1e6 + count(usage.server_tool_use?.web_search_requests) * WEB_SEARCH_USD;
7883
8021
  }
8022
+ function longPromptCost(base, usage, count) {
8023
+ const tier = LONG_PROMPT_COSTS[base];
8024
+ if (!tier) return null;
8025
+ const promptTokens = count(usage.input_tokens) + count(usage.cache_read_input_tokens) + count(usage.cache_creation_input_tokens);
8026
+ if (promptTokens <= tier.aboveTokens) return null;
8027
+ return {
8028
+ input: tier.cost.input,
8029
+ output: tier.cost.output,
8030
+ cache: { read: tier.cost.cacheRead, write: tier.cost.cacheWrite }
8031
+ };
8032
+ }
7884
8033
 
7885
8034
  // src/agent-models.ts
7886
8035
  var AGENT_DIR_NAMES = ["agents", "agent"];
@@ -8058,13 +8207,13 @@ async function readAgentMarkdownRecords(directories) {
8058
8207
  const name = entry.slice(0, -3);
8059
8208
  if (records2[name]) continue;
8060
8209
  try {
8061
- const text = await readFile(path8.join(directory, entry), "utf8");
8210
+ const text = await readFile(path9.join(directory, entry), "utf8");
8062
8211
  records2[name] = parseAgentFrontmatter(text);
8063
8212
  const prompt = agentMarkdownBody(text);
8064
8213
  if (prompt) records2[name].prompt = prompt;
8065
8214
  } catch (err) {
8066
8215
  log.debug("failed to read agent markdown", {
8067
- file: path8.join(directory, entry),
8216
+ file: path9.join(directory, entry),
8068
8217
  error: String(err)
8069
8218
  });
8070
8219
  }
@@ -8076,12 +8225,12 @@ function agentDirectories(home, projectDirectory) {
8076
8225
  const directories = [];
8077
8226
  if (projectDirectory) {
8078
8227
  for (const name of AGENT_DIR_NAMES) {
8079
- directories.push(path8.join(projectDirectory, ".opencode", name));
8228
+ directories.push(path9.join(projectDirectory, ".opencode", name));
8080
8229
  }
8081
8230
  }
8082
8231
  if (home) {
8083
8232
  for (const name of AGENT_DIR_NAMES) {
8084
- directories.push(path8.join(home, ".config", "opencode", name));
8233
+ directories.push(path9.join(home, ".config", "opencode", name));
8085
8234
  }
8086
8235
  }
8087
8236
  return directories;
@@ -8397,6 +8546,17 @@ function clipWithMarker(text, max) {
8397
8546
  return `${text.slice(0, max)}
8398
8547
  \u2026[truncated ${text.length - max} chars]`;
8399
8548
  }
8549
+ var MAX_REPLAY_MESSAGE_CHARS = 2e3;
8550
+ var MAX_REPLAY_LAST_REPLY_CHARS = 12e3;
8551
+ var MAX_REPLAY_HEAD_CHARS = 1e3;
8552
+ function clipMiddle(text, max) {
8553
+ if (text.length <= max) return text;
8554
+ const head = Math.min(Math.floor(max / 4), MAX_REPLAY_HEAD_CHARS);
8555
+ const tail = max - head;
8556
+ return `${text.slice(0, head)}
8557
+ \u2026[${text.length - max} chars omitted]\u2026
8558
+ ${text.slice(-tail)}`;
8559
+ }
8400
8560
  function renderToolInput(input) {
8401
8561
  let raw;
8402
8562
  try {
@@ -8465,13 +8625,20 @@ function compactConversationHistory(prompt, opts = {}) {
8465
8625
  return null;
8466
8626
  }
8467
8627
  const historyParts = [];
8628
+ let lastAssistant = -1;
8629
+ for (let i = conversationMessages.length - 2; i >= 0; i--) {
8630
+ if (conversationMessages[i].role === "assistant") {
8631
+ lastAssistant = i;
8632
+ break;
8633
+ }
8634
+ }
8468
8635
  for (let i = 0; i < conversationMessages.length - 1; i++) {
8469
8636
  const msg = conversationMessages[i];
8470
8637
  const role = msg.role === "user" ? "User" : msg.role === "assistant" ? "Assistant" : "Tool";
8471
8638
  const { text } = renderMessageContentForCompaction(msg);
8472
8639
  if (text.trim()) {
8473
- const truncated = text.length > 2e3 ? text.slice(0, 2e3) + "..." : text;
8474
- historyParts.push(`${role}: ${truncated}`);
8640
+ const max = i === lastAssistant ? MAX_REPLAY_LAST_REPLY_CHARS : MAX_REPLAY_MESSAGE_CHARS;
8641
+ historyParts.push(`${role}: ${clipMiddle(text, max)}`);
8475
8642
  }
8476
8643
  }
8477
8644
  if (historyParts.length === 0) {
@@ -8681,14 +8848,14 @@ function getTrailingUserMessages(prompt, opts = {}) {
8681
8848
 
8682
8849
  // src/skill-bridge.ts
8683
8850
  import * as crypto3 from "crypto";
8684
- import * as fs7 from "fs";
8685
- import * as os3 from "os";
8686
- import * as path9 from "path";
8851
+ import * as fs8 from "fs";
8852
+ import * as os4 from "os";
8853
+ import * as path10 from "path";
8687
8854
  import { fileURLToPath as fileURLToPath3 } from "url";
8688
8855
  function bundledSkillsDir() {
8689
8856
  try {
8690
8857
  const here = fileURLToPath3(import.meta.url);
8691
- const dir = path9.resolve(path9.dirname(here), "..", "skills");
8858
+ const dir = path10.resolve(path10.dirname(here), "..", "skills");
8692
8859
  return dirExists2(dir) ? dir : null;
8693
8860
  } catch {
8694
8861
  return null;
@@ -8696,16 +8863,16 @@ function bundledSkillsDir() {
8696
8863
  }
8697
8864
  function dirExists2(p) {
8698
8865
  try {
8699
- return fs7.statSync(p).isDirectory();
8866
+ return fs8.statSync(p).isDirectory();
8700
8867
  } catch {
8701
8868
  return false;
8702
8869
  }
8703
8870
  }
8704
8871
  function realPath(p) {
8705
8872
  try {
8706
- return fs7.realpathSync(p);
8873
+ return fs8.realpathSync(p);
8707
8874
  } catch {
8708
- return path9.resolve(p);
8875
+ return path10.resolve(p);
8709
8876
  }
8710
8877
  }
8711
8878
  function envEnabled(name) {
@@ -8726,10 +8893,10 @@ function declaredSkillName(source) {
8726
8893
  return SAFE_SKILL_NAME.test(name) ? name : null;
8727
8894
  }
8728
8895
  function readSkill(root, entry) {
8729
- const dir = path9.join(root, entry);
8896
+ const dir = path10.join(root, entry);
8730
8897
  let source;
8731
8898
  try {
8732
- source = fs7.readFileSync(path9.join(dir, "SKILL.md"), "utf8");
8899
+ source = fs8.readFileSync(path10.join(dir, "SKILL.md"), "utf8");
8733
8900
  } catch {
8734
8901
  return null;
8735
8902
  }
@@ -8747,47 +8914,47 @@ function skillRoots(cwd) {
8747
8914
  const roots = [];
8748
8915
  const seen = /* @__PURE__ */ new Set();
8749
8916
  const push = (p) => {
8750
- const abs = path9.resolve(p);
8917
+ const abs = path10.resolve(p);
8751
8918
  if (seen.has(abs)) return;
8752
8919
  seen.add(abs);
8753
8920
  if (dirExists2(abs)) roots.push(abs);
8754
8921
  };
8755
- const home = os3.homedir();
8756
- const homeDir = home ? path9.resolve(home) : null;
8757
- let current = path9.resolve(cwd);
8922
+ const home = os4.homedir();
8923
+ const homeDir = home ? path10.resolve(home) : null;
8924
+ let current = path10.resolve(cwd);
8758
8925
  while (true) {
8759
- push(path9.join(current, ".opencode", "skills"));
8926
+ push(path10.join(current, ".opencode", "skills"));
8760
8927
  if (current !== homeDir) {
8761
- push(path9.join(current, ".claude", "skills"));
8762
- push(path9.join(current, ".agents", "skills"));
8928
+ push(path10.join(current, ".claude", "skills"));
8929
+ push(path10.join(current, ".agents", "skills"));
8763
8930
  }
8764
- const parent = path9.dirname(current);
8931
+ const parent = path10.dirname(current);
8765
8932
  if (parent === current) break;
8766
8933
  current = parent;
8767
8934
  }
8768
- if (homeDir) push(path9.join(homeDir, ".opencode", "skills"));
8935
+ if (homeDir) push(path10.join(homeDir, ".opencode", "skills"));
8769
8936
  const envDir = process.env.OPENCODE_CONFIG_DIR;
8770
8937
  if (envDir) {
8771
- push(path9.join(envDir, "skills"));
8772
- push(path9.join(envDir, "skill"));
8938
+ push(path10.join(envDir, "skills"));
8939
+ push(path10.join(envDir, "skill"));
8773
8940
  }
8774
- const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path9.join(homeDir, ".config") : null);
8941
+ const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path10.join(homeDir, ".config") : null);
8775
8942
  if (xdg) {
8776
- push(path9.join(xdg, "opencode", "skills"));
8777
- push(path9.join(xdg, "opencode", "skill"));
8943
+ push(path10.join(xdg, "opencode", "skills"));
8944
+ push(path10.join(xdg, "opencode", "skill"));
8778
8945
  }
8779
8946
  if (homeDir && !envEnabled("OPENCODE_DISABLE_EXTERNAL_SKILLS")) {
8780
8947
  if (!envEnabled("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS")) {
8781
- push(path9.join(homeDir, ".claude", "skills"));
8948
+ push(path10.join(homeDir, ".claude", "skills"));
8782
8949
  }
8783
- push(path9.join(homeDir, ".agents", "skills"));
8950
+ push(path10.join(homeDir, ".agents", "skills"));
8784
8951
  }
8785
8952
  return roots;
8786
8953
  }
8787
8954
  function collectSkills(root, claimed, found) {
8788
8955
  let entries;
8789
8956
  try {
8790
- entries = fs7.readdirSync(root, { withFileTypes: true });
8957
+ entries = fs8.readdirSync(root, { withFileTypes: true });
8791
8958
  } catch {
8792
8959
  return;
8793
8960
  }
@@ -8827,23 +8994,23 @@ function discoverBundledSkills() {
8827
8994
  }
8828
8995
  function claudeConfigDir(configDir) {
8829
8996
  const explicit = configDir ?? process.env.CLAUDE_CONFIG_DIR;
8830
- if (explicit) return path9.resolve(expandHome(explicit));
8831
- const home = os3.homedir();
8832
- return home ? path9.resolve(home, ".claude") : null;
8997
+ if (explicit) return path10.resolve(expandHome(explicit));
8998
+ const home = os4.homedir();
8999
+ return home ? path10.resolve(home, ".claude") : null;
8833
9000
  }
8834
9001
  function installedPluginSkillRoots(configDir, cwd) {
8835
9002
  if (!configDir) return [];
8836
9003
  let parsed;
8837
9004
  try {
8838
9005
  parsed = JSON.parse(
8839
- fs7.readFileSync(path9.join(configDir, "plugins", "installed_plugins.json"), "utf8")
9006
+ fs8.readFileSync(path10.join(configDir, "plugins", "installed_plugins.json"), "utf8")
8840
9007
  );
8841
9008
  } catch {
8842
9009
  return [];
8843
9010
  }
8844
9011
  const plugins = parsed?.plugins;
8845
9012
  if (!plugins || typeof plugins !== "object") return [];
8846
- const workspace = path9.resolve(cwd);
9013
+ const workspace = path10.resolve(cwd);
8847
9014
  const roots = [];
8848
9015
  for (const installs of Object.values(plugins)) {
8849
9016
  if (!Array.isArray(installs)) continue;
@@ -8851,28 +9018,28 @@ function installedPluginSkillRoots(configDir, cwd) {
8851
9018
  const entry = install;
8852
9019
  if (typeof entry?.installPath !== "string" || !entry.installPath) continue;
8853
9020
  if (entry.scope !== "user" && !coversWorkspace(entry.projectPath, workspace)) continue;
8854
- roots.push(path9.join(entry.installPath, "skills"));
9021
+ roots.push(path10.join(entry.installPath, "skills"));
8855
9022
  }
8856
9023
  }
8857
9024
  return roots;
8858
9025
  }
8859
9026
  function coversWorkspace(projectPath, workspace) {
8860
9027
  if (typeof projectPath !== "string" || !projectPath) return false;
8861
- const root = path9.resolve(projectPath);
9028
+ const root = path10.resolve(projectPath);
8862
9029
  if (root === workspace) return true;
8863
- const rel = path9.relative(root, workspace);
8864
- return rel !== "" && !rel.startsWith("..") && !path9.isAbsolute(rel);
9030
+ const rel = path10.relative(root, workspace);
9031
+ return rel !== "" && !rel.startsWith("..") && !path10.isAbsolute(rel);
8865
9032
  }
8866
9033
  function discoverNativeClaudeSkills(opts) {
8867
9034
  const found = [];
8868
9035
  const scanned = /* @__PURE__ */ new Set();
8869
9036
  const scan = (root, scope) => {
8870
- const abs = path9.resolve(root);
9037
+ const abs = path10.resolve(root);
8871
9038
  if (scanned.has(abs)) return;
8872
9039
  scanned.add(abs);
8873
9040
  let entries;
8874
9041
  try {
8875
- entries = fs7.readdirSync(abs, { withFileTypes: true });
9042
+ entries = fs8.readdirSync(abs, { withFileTypes: true });
8876
9043
  } catch {
8877
9044
  return;
8878
9045
  }
@@ -8884,13 +9051,13 @@ function discoverNativeClaudeSkills(opts) {
8884
9051
  }
8885
9052
  };
8886
9053
  const configDir = claudeConfigDir(opts.configDir);
8887
- if (configDir) scan(path9.join(configDir, "skills"), "user");
8888
- const home = os3.homedir();
8889
- const homeDir = home ? path9.resolve(home) : null;
8890
- let current = path9.resolve(opts.cwd);
9054
+ if (configDir) scan(path10.join(configDir, "skills"), "user");
9055
+ const home = os4.homedir();
9056
+ const homeDir = home ? path10.resolve(home) : null;
9057
+ let current = path10.resolve(opts.cwd);
8891
9058
  while (true) {
8892
- if (current !== homeDir) scan(path9.join(current, ".claude", "skills"), "project");
8893
- const parent = path9.dirname(current);
9059
+ if (current !== homeDir) scan(path10.join(current, ".claude", "skills"), "project");
9060
+ const parent = path10.dirname(current);
8894
9061
  if (parent === current) break;
8895
9062
  current = parent;
8896
9063
  }
@@ -8924,10 +9091,10 @@ function dropNativelyLoadedSkills(skills, native) {
8924
9091
  }
8925
9092
  function readBundledSkillInfos() {
8926
9093
  return discoverBundledSkills().flatMap((skill) => {
8927
- const file = path9.join(skill.dir, "SKILL.md");
9094
+ const file = path10.join(skill.dir, "SKILL.md");
8928
9095
  let text;
8929
9096
  try {
8930
- text = fs7.readFileSync(file, "utf8");
9097
+ text = fs8.readFileSync(file, "utf8");
8931
9098
  } catch {
8932
9099
  return [];
8933
9100
  }
@@ -8951,15 +9118,15 @@ function readBundledSkillInfos() {
8951
9118
  }
8952
9119
  function linkSkill(source, target) {
8953
9120
  try {
8954
- fs7.symlinkSync(source, target, process.platform === "win32" ? "junction" : "dir");
9121
+ fs8.symlinkSync(source, target, process.platform === "win32" ? "junction" : "dir");
8955
9122
  return;
8956
9123
  } catch {
8957
- fs7.cpSync(source, target, { recursive: true, dereference: true });
9124
+ fs8.cpSync(source, target, { recursive: true, dereference: true });
8958
9125
  }
8959
9126
  }
8960
9127
  function fileExists2(p) {
8961
9128
  try {
8962
- return fs7.statSync(p).isFile();
9129
+ return fs8.statSync(p).isFile();
8963
9130
  } catch {
8964
9131
  return false;
8965
9132
  }
@@ -8968,14 +9135,14 @@ function buildSkillPluginDir(skills) {
8968
9135
  if (skills.length === 0) return null;
8969
9136
  const fingerprint = skills.map((s) => `${s.name}\0${s.dir}`).join("\n");
8970
9137
  const hash = crypto3.createHash("sha256").update(fingerprint).digest("hex").slice(0, 12);
8971
- const root = path9.join(pluginTmpDir(), `skills-${hash}`);
8972
- const manifest = path9.join(root, ".claude-plugin", "plugin.json");
9138
+ const root = path10.join(pluginTmpDir(), `skills-${hash}`);
9139
+ const manifest = path10.join(root, ".claude-plugin", "plugin.json");
8973
9140
  if (fileExists2(manifest)) return root;
8974
9141
  try {
8975
- fs7.rmSync(root, { recursive: true, force: true });
8976
- fs7.mkdirSync(path9.join(root, ".claude-plugin"), { recursive: true });
8977
- fs7.mkdirSync(path9.join(root, "skills"), { recursive: true });
8978
- fs7.writeFileSync(
9142
+ fs8.rmSync(root, { recursive: true, force: true });
9143
+ fs8.mkdirSync(path10.join(root, ".claude-plugin"), { recursive: true });
9144
+ fs8.mkdirSync(path10.join(root, "skills"), { recursive: true });
9145
+ fs8.writeFileSync(
8979
9146
  manifest,
8980
9147
  JSON.stringify(
8981
9148
  {
@@ -8988,7 +9155,7 @@ function buildSkillPluginDir(skills) {
8988
9155
  { encoding: "utf8", mode: 384 }
8989
9156
  );
8990
9157
  for (const skill of skills) {
8991
- linkSkill(skill.dir, path9.join(root, "skills", skill.name));
9158
+ linkSkill(skill.dir, path10.join(root, "skills", skill.name));
8992
9159
  }
8993
9160
  } catch (err) {
8994
9161
  log.warn("failed to stage opencode skill plugin dir", {
@@ -9004,7 +9171,7 @@ function registerBundledSkillPath(config) {
9004
9171
  if (!dir) return false;
9005
9172
  config.skills ??= {};
9006
9173
  const paths = config.skills.paths ??= [];
9007
- if (paths.some((p) => path9.resolve(p) === dir)) return false;
9174
+ if (paths.some((p) => path10.resolve(p) === dir)) return false;
9008
9175
  paths.push(dir);
9009
9176
  return true;
9010
9177
  }
@@ -9054,36 +9221,36 @@ import { EventEmitter as EventEmitter4 } from "events";
9054
9221
  import { randomUUID as randomUUID5 } from "crypto";
9055
9222
  import { existsSync as existsSync4, writeFileSync as writeFileSync5 } from "fs";
9056
9223
  import { unlink as unlink2 } from "fs/promises";
9057
- import { join as join10 } from "path";
9224
+ import { join as join11 } from "path";
9058
9225
 
9059
9226
  // src/claude-session-bun.ts
9060
- import * as os4 from "os";
9061
- import * as fs8 from "fs";
9062
- import * as path10 from "path";
9227
+ import * as os5 from "os";
9228
+ import * as fs9 from "fs";
9229
+ import * as path11 from "path";
9063
9230
  import { execFileSync } from "child_process";
9064
9231
  import { randomUUID as randomUUID4 } from "crypto";
9065
9232
  import { StringDecoder } from "string_decoder";
9066
9233
  function resolveClaude(cmd = "claude") {
9067
- if (path10.isAbsolute(cmd) && fs8.existsSync(cmd)) return cmd;
9234
+ if (path11.isAbsolute(cmd) && fs9.existsSync(cmd)) return cmd;
9068
9235
  const viaBun = Bun.which(cmd);
9069
9236
  if (viaBun) return viaBun;
9070
- const isWin = os4.platform() === "win32";
9237
+ const isWin = os5.platform() === "win32";
9071
9238
  try {
9072
9239
  const out = execFileSync(isWin ? "where" : "which", [cmd], {
9073
9240
  encoding: "utf8",
9074
9241
  stdio: ["ignore", "pipe", "ignore"]
9075
9242
  });
9076
- const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) => fs8.existsSync(p));
9243
+ const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) => fs9.existsSync(p));
9077
9244
  if (first) return first;
9078
9245
  } catch {
9079
9246
  }
9080
9247
  throw new Error(`Could not resolve command on PATH: ${cmd}`);
9081
9248
  }
9082
9249
  function encodeCwd(cwd) {
9083
- const resolved = path10.resolve(cwd);
9250
+ const resolved = path11.resolve(cwd);
9084
9251
  let real = resolved;
9085
9252
  try {
9086
- real = fs8.realpathSync.native(resolved);
9253
+ real = fs9.realpathSync.native(resolved);
9087
9254
  } catch {
9088
9255
  }
9089
9256
  return real.replace(/[^a-zA-Z0-9]/g, "-");
@@ -9091,7 +9258,7 @@ function encodeCwd(cwd) {
9091
9258
  var bunPtySpawner = (argv, opts) => {
9092
9259
  const [command, ...args] = argv;
9093
9260
  const resolved = resolveClaude(command ?? "claude");
9094
- if (os4.platform() === "win32" && isBatchFile(resolved)) {
9261
+ if (os5.platform() === "win32" && isBatchFile(resolved)) {
9095
9262
  throw new Error(
9096
9263
  `The interactive transport cannot run a Windows batch shim (${resolved}). Point \`cliPath\` at claude.exe, or use the default headless transport.`
9097
9264
  );
@@ -9199,10 +9366,10 @@ function classifyScreen(text) {
9199
9366
  return null;
9200
9367
  }
9201
9368
  function interactiveTranscriptPath(opts) {
9202
- return path10.join(
9369
+ return path11.join(
9203
9370
  resolveConfigDir(opts.configDir),
9204
9371
  "projects",
9205
- encodeCwd(path10.resolve(opts.cwd)),
9372
+ encodeCwd(path11.resolve(opts.cwd)),
9206
9373
  `${opts.sessionId}.jsonl`
9207
9374
  );
9208
9375
  }
@@ -9380,7 +9547,7 @@ var TranscriptCursor = class {
9380
9547
  const filePath = this.resolvePath();
9381
9548
  let stat;
9382
9549
  try {
9383
- stat = fs8.statSync(filePath);
9550
+ stat = fs9.statSync(filePath);
9384
9551
  } catch {
9385
9552
  return;
9386
9553
  }
@@ -9396,13 +9563,13 @@ var TranscriptCursor = class {
9396
9563
  if (stat.size <= this.offset) return;
9397
9564
  let fd = null;
9398
9565
  try {
9399
- fd = fs8.openSync(filePath, "r");
9566
+ fd = fs9.openSync(filePath, "r");
9400
9567
  const buffer = Buffer.allocUnsafe(
9401
9568
  Math.min(TRANSCRIPT_CHUNK_BYTES, stat.size - this.offset)
9402
9569
  );
9403
9570
  while (this.offset < stat.size) {
9404
9571
  const want = Math.min(buffer.length, stat.size - this.offset);
9405
- const read = fs8.readSync(fd, buffer, 0, want, this.offset);
9572
+ const read = fs9.readSync(fd, buffer, 0, want, this.offset);
9406
9573
  this.stats.reads++;
9407
9574
  if (read <= 0) break;
9408
9575
  this.offset += read;
@@ -9413,7 +9580,7 @@ var TranscriptCursor = class {
9413
9580
  } finally {
9414
9581
  if (fd !== null) {
9415
9582
  try {
9416
- fs8.closeSync(fd);
9583
+ fs9.closeSync(fd);
9417
9584
  } catch {
9418
9585
  }
9419
9586
  }
@@ -9443,12 +9610,12 @@ function isOwnPromptRecord(rec, normalized) {
9443
9610
  }
9444
9611
  function resolveConfigDir(configDir) {
9445
9612
  const value = configDir ?? process.env.CLAUDE_CONFIG_DIR;
9446
- if (!value) return path10.join(os4.homedir(), ".claude");
9447
- if (value === "~") return os4.homedir();
9613
+ if (!value) return path11.join(os5.homedir(), ".claude");
9614
+ if (value === "~") return os5.homedir();
9448
9615
  if (value.startsWith("~/") || value.startsWith("~\\")) {
9449
- return path10.join(os4.homedir(), value.slice(2));
9616
+ return path11.join(os5.homedir(), value.slice(2));
9450
9617
  }
9451
- return path10.resolve(value);
9618
+ return path11.resolve(value);
9452
9619
  }
9453
9620
  function fatalScreenMessage(kind, configDir, cwd) {
9454
9621
  if (kind === "trust") {
@@ -9512,7 +9679,7 @@ var ClaudeSession = class {
9512
9679
  abandonTurn = false;
9513
9680
  o;
9514
9681
  constructor(opts = {}) {
9515
- this.cwd = path10.resolve(opts.cwd ?? process.cwd());
9682
+ this.cwd = path11.resolve(opts.cwd ?? process.cwd());
9516
9683
  this.configDir = resolveConfigDir(opts.configDir);
9517
9684
  this.explicitConfigDir = opts.configDir ? this.configDir : void 0;
9518
9685
  this.signal = opts.signal;
@@ -9524,7 +9691,7 @@ var ClaudeSession = class {
9524
9691
  this.onExit = opts.onExit;
9525
9692
  this.spawnPty = opts.spawnPty ?? bunPtySpawner;
9526
9693
  this.sessionId = opts.resumeSessionId ?? randomUUID4();
9527
- this.jsonlPath = path10.join(
9694
+ this.jsonlPath = path11.join(
9528
9695
  this.configDir,
9529
9696
  "projects",
9530
9697
  encodeCwd(this.cwd),
@@ -10235,7 +10402,7 @@ ${text2}`
10235
10402
  }
10236
10403
  function stageImage(data, extension) {
10237
10404
  try {
10238
- const file = join10(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
10405
+ const file = join11(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
10239
10406
  writeFileSync5(file, data, { mode: 384, flag: "wx" });
10240
10407
  return file;
10241
10408
  } catch (err) {
@@ -10720,7 +10887,7 @@ function spawnInteractiveProcess(opts) {
10720
10887
  import { randomUUID as randomUUID6 } from "crypto";
10721
10888
  import { constants as fsConstants } from "fs";
10722
10889
  import { chmod as chmod2, copyFile as copyFile2, lstat as lstat2, mkdir as mkdir2 } from "fs/promises";
10723
- import path11 from "path";
10890
+ import path12 from "path";
10724
10891
  var transcriptPath = interactiveTranscriptPath;
10725
10892
  function configDirForAccount(account) {
10726
10893
  const normalized = normalizeAccountName(account || DEFAULT_ACCOUNT);
@@ -10748,7 +10915,7 @@ async function carryTranscriptToAccount(input) {
10748
10915
  const carriedId = renamed ? (input.newSessionId ?? randomUUID6)() : sessionId;
10749
10916
  const target = renamed ? transcriptPath({ configDir: targetConfigDir, cwd, sessionId: carriedId }) : targetOwn;
10750
10917
  try {
10751
- await mkdir2(path11.dirname(target), { recursive: true, mode: 448 });
10918
+ await mkdir2(path12.dirname(target), { recursive: true, mode: 448 });
10752
10919
  await copyFile2(live.file, target, fsConstants.COPYFILE_EXCL);
10753
10920
  await chmod2(target, 384);
10754
10921
  } catch (err) {
@@ -10768,7 +10935,7 @@ function dedupe2(dirs) {
10768
10935
  const seen = /* @__PURE__ */ new Set();
10769
10936
  const out = [];
10770
10937
  for (const dir of dirs) {
10771
- const resolved = path11.resolve(dir);
10938
+ const resolved = path12.resolve(dir);
10772
10939
  if (seen.has(resolved)) continue;
10773
10940
  seen.add(resolved);
10774
10941
  out.push(resolved);
@@ -10777,13 +10944,13 @@ function dedupe2(dirs) {
10777
10944
  }
10778
10945
 
10779
10946
  // src/prompts.ts
10780
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
10781
- import { homedir as homedir8 } from "os";
10947
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
10948
+ import { homedir as homedir9 } from "os";
10782
10949
  import { randomUUID as randomUUID7 } from "crypto";
10783
- import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join11 } from "path";
10784
- function readPromptFileIfPresent(path14) {
10950
+ import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join12 } from "path";
10951
+ function readPromptFileIfPresent(path15) {
10785
10952
  try {
10786
- const content = readFileSync6(path14, "utf8").trim();
10953
+ const content = readFileSync7(path15, "utf8").trim();
10787
10954
  return content || void 0;
10788
10955
  } catch {
10789
10956
  return void 0;
@@ -10792,7 +10959,7 @@ function readPromptFileIfPresent(path14) {
10792
10959
  function nearestWorkspaceAgentsPrompt(cwd) {
10793
10960
  let dir = cwd;
10794
10961
  while (true) {
10795
- const content = readPromptFileIfPresent(join11(dir, "AGENTS.md"));
10962
+ const content = readPromptFileIfPresent(join12(dir, "AGENTS.md"));
10796
10963
  if (content) return content;
10797
10964
  const parent = dirname6(dir);
10798
10965
  if (parent === dir) return void 0;
@@ -10888,9 +11055,9 @@ function extractSystemMessages(prompt) {
10888
11055
  }
10889
11056
  var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLAUDE.local.md"]);
10890
11057
  function userAuthoredInstructions(forwarded, opts = {}) {
10891
- const readFile2 = opts.readFile ?? ((path14) => {
11058
+ const readFile2 = opts.readFile ?? ((path15) => {
10892
11059
  try {
10893
- return readFileSync6(path14, "utf8");
11060
+ return readFileSync7(path15, "utf8");
10894
11061
  } catch {
10895
11062
  return null;
10896
11063
  }
@@ -10902,16 +11069,16 @@ function userAuthoredInstructions(forwarded, opts = {}) {
10902
11069
  }
10903
11070
  const seen = /* @__PURE__ */ new Set();
10904
11071
  for (const match of forwarded.matchAll(/^Instructions from: (.+)$/gm)) {
10905
- const path14 = match[1].trim();
10906
- if (seen.has(path14) || !isAbsolute3(path14)) continue;
10907
- if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path14))) continue;
10908
- seen.add(path14);
10909
- const file = readFile2(path14);
11072
+ const path15 = match[1].trim();
11073
+ if (seen.has(path15) || !isAbsolute3(path15)) continue;
11074
+ if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path15))) continue;
11075
+ seen.add(path15);
11076
+ const file = readFile2(path15);
10910
11077
  if (!file?.trim()) continue;
10911
11078
  const start = match.index + match[0].length + 1;
10912
11079
  const content = forwarded.startsWith(file, start) ? file : forwarded.startsWith(file.trimEnd(), start) ? file.trimEnd() : null;
10913
11080
  if (content === null) continue;
10914
- out.push({ text: `Instructions from: ${path14}
11081
+ out.push({ text: `Instructions from: ${path15}
10915
11082
  ${content}`, content });
10916
11083
  }
10917
11084
  return out;
@@ -10931,8 +11098,8 @@ ${options.compressionSummary.trim()}`
10931
11098
  for (const s of extraSystemContent) {
10932
11099
  if (s.trim()) parts.push(s.trim());
10933
11100
  }
10934
- const configRoot = process.env.XDG_CONFIG_HOME ?? join11(homedir8(), ".config");
10935
- const globalAgents = readPromptFileIfPresent(join11(configRoot, "opencode", "AGENTS.md"));
11101
+ const configRoot = process.env.XDG_CONFIG_HOME ?? join12(homedir9(), ".config");
11102
+ const globalAgents = readPromptFileIfPresent(join12(configRoot, "opencode", "AGENTS.md"));
10936
11103
  const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
10937
11104
  const forwarded = extraSystemContent.join("\n\n");
10938
11105
  const pushGlobal = !!globalAgents && !forwarded.includes(globalAgents);
@@ -10948,10 +11115,10 @@ ${options.compressionSummary.trim()}`
10948
11115
  if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
10949
11116
  const content = parts.join("\n\n");
10950
11117
  if (!content) return void 0;
10951
- const path14 = join11(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
11118
+ const path15 = join12(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
10952
11119
  try {
10953
- writeFileSync6(path14, content, { encoding: "utf8", mode: 384 });
10954
- return path14;
11120
+ writeFileSync6(path15, content, { encoding: "utf8", mode: 384 });
11121
+ return path15;
10955
11122
  } catch (err) {
10956
11123
  log.warn("failed to write system prompt file", { error: String(err) });
10957
11124
  return void 0;
@@ -11378,7 +11545,9 @@ function effectiveMcpConfig(config, cwd, proxyConfigPath, runtimeStatus, exclude
11378
11545
  let bridgedHash = null;
11379
11546
  let allEnabledServerNames = [];
11380
11547
  if (config.bridgeOpencodeMcp !== false) {
11381
- const bridged = bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, config.hostApi);
11548
+ const bridged = bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, config.hostApi, {
11549
+ oauthTokens: config.bridgeMcpOauthTokens === true
11550
+ });
11382
11551
  if (bridged) {
11383
11552
  if (bridged.path) paths.push(bridged.path);
11384
11553
  bridgedHash = bridged.hash;
@@ -14082,7 +14251,7 @@ var ClaudeCodeLanguageModel = class {
14082
14251
  let replayReason = "no-session-for-key";
14083
14252
  if (this.config.resumeAfterRestart !== false && includeHistoryContext && !compactionMode && !forkFromClaudeSessionId && failoverAnswer?.kind !== "switch") {
14084
14253
  const configDir = resolveConfigDir(this.config.configDir);
14085
- const transcriptPath2 = (id) => join12(configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
14254
+ const transcriptPath2 = (id) => join13(configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
14086
14255
  const resumePoint = findResumePoint({
14087
14256
  sessionKey: sk,
14088
14257
  prompt: options.prompt,
@@ -15338,8 +15507,8 @@ var ClaudeCodeLanguageModel = class {
15338
15507
  };
15339
15508
 
15340
15509
  // src/account-tier.ts
15341
- import { readFileSync as readFileSync7 } from "fs";
15342
- import path12 from "path";
15510
+ import { readFileSync as readFileSync8 } from "fs";
15511
+ import path13 from "path";
15343
15512
  var ORGANIZATION_TYPE_LABELS = {
15344
15513
  claude_max: "Max",
15345
15514
  claude_pro: "Pro",
@@ -15368,7 +15537,7 @@ function readAccountTier(account) {
15368
15537
  return label;
15369
15538
  }
15370
15539
  function readTierFromConfigDir(configDir, allowLegacyFallback) {
15371
- const candidates = [path12.join(configDir, ".claude.json")];
15540
+ const candidates = [path13.join(configDir, ".claude.json")];
15372
15541
  if (allowLegacyFallback) candidates.push(expandHome("~/.claude.json"));
15373
15542
  for (const candidate of candidates) {
15374
15543
  const oauth = readOauthAccount(candidate);
@@ -15381,7 +15550,7 @@ function readTierFromConfigDir(configDir, allowLegacyFallback) {
15381
15550
  function readOauthAccount(file) {
15382
15551
  let raw;
15383
15552
  try {
15384
- raw = readFileSync7(file, "utf8");
15553
+ raw = readFileSync8(file, "utf8");
15385
15554
  } catch {
15386
15555
  return void 0;
15387
15556
  }
@@ -15410,13 +15579,13 @@ function tierLabel(oauthAccount) {
15410
15579
  import {
15411
15580
  existsSync as existsSync5,
15412
15581
  mkdirSync as mkdirSync5,
15413
- readFileSync as readFileSync8,
15582
+ readFileSync as readFileSync9,
15414
15583
  realpathSync as realpathSync4,
15415
15584
  rmSync as rmSync3,
15416
15585
  writeFileSync as writeFileSync7
15417
15586
  } from "fs";
15418
- import { homedir as homedir9 } from "os";
15419
- import { dirname as dirname7, join as join13, resolve as resolve5 } from "path";
15587
+ import { homedir as homedir10 } from "os";
15588
+ import { dirname as dirname7, join as join14, resolve as resolve5 } from "path";
15420
15589
  import { fileURLToPath as fileURLToPath4 } from "url";
15421
15590
  var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
15422
15591
  var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
@@ -15424,20 +15593,20 @@ var alreadyRan = false;
15424
15593
  function candidateCacheRoots() {
15425
15594
  const xdg = process.env.XDG_CACHE_HOME;
15426
15595
  return [
15427
- xdg ? join13(xdg, "opencode") : null,
15428
- join13(homedir9(), ".cache", "opencode"),
15429
- join13(homedir9(), "Library", "Caches", "opencode")
15596
+ xdg ? join14(xdg, "opencode") : null,
15597
+ join14(homedir10(), ".cache", "opencode"),
15598
+ join14(homedir10(), "Library", "Caches", "opencode")
15430
15599
  ].filter((p) => Boolean(p));
15431
15600
  }
15432
15601
  function userOpencodeJsonPath() {
15433
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join13(homedir9(), ".config");
15434
- return join13(xdgConfig, "opencode", "opencode.json");
15602
+ const xdgConfig = process.env.XDG_CONFIG_HOME ?? join14(homedir10(), ".config");
15603
+ return join14(xdgConfig, "opencode", "opencode.json");
15435
15604
  }
15436
15605
  function userIntendsToUseUnscoped() {
15437
15606
  const cfg = userOpencodeJsonPath();
15438
15607
  if (!existsSync5(cfg)) return false;
15439
15608
  try {
15440
- const json = JSON.parse(readFileSync8(cfg, "utf8"));
15609
+ const json = JSON.parse(readFileSync9(cfg, "utf8"));
15441
15610
  const plugins = json.plugin;
15442
15611
  if (!Array.isArray(plugins)) return false;
15443
15612
  return plugins.some(
@@ -15458,19 +15627,19 @@ function ourLoadedDir() {
15458
15627
  function ourVersion(dir) {
15459
15628
  if (!dir) return null;
15460
15629
  try {
15461
- const pkg = JSON.parse(readFileSync8(join13(dir, "package.json"), "utf8"));
15630
+ const pkg = JSON.parse(readFileSync9(join14(dir, "package.json"), "utf8"));
15462
15631
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
15463
15632
  } catch {
15464
15633
  return null;
15465
15634
  }
15466
15635
  }
15467
15636
  function markerPath() {
15468
- const stateRoot = process.env.XDG_STATE_HOME ?? join13(homedir9(), ".local", "state");
15469
- return join13(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
15637
+ const stateRoot = process.env.XDG_STATE_HOME ?? join14(homedir10(), ".local", "state");
15638
+ return join14(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
15470
15639
  }
15471
15640
  function markerVersion() {
15472
15641
  try {
15473
- const marker = JSON.parse(readFileSync8(markerPath(), "utf8"));
15642
+ const marker = JSON.parse(readFileSync9(markerPath(), "utf8"));
15474
15643
  return typeof marker.version === "string" ? marker.version : null;
15475
15644
  } catch {
15476
15645
  return null;
@@ -15515,7 +15684,7 @@ function cleanupStaleUnscopedInstall() {
15515
15684
  }
15516
15685
  function cleanupOne(cacheRoot, ourDir) {
15517
15686
  if (!existsSync5(cacheRoot)) return;
15518
- const stalePath = join13(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
15687
+ const stalePath = join14(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
15519
15688
  if (!existsSync5(stalePath)) return;
15520
15689
  let realStalePath = stalePath;
15521
15690
  try {
@@ -15523,11 +15692,11 @@ function cleanupOne(cacheRoot, ourDir) {
15523
15692
  } catch {
15524
15693
  }
15525
15694
  if (ourDir && realStalePath === ourDir) return;
15526
- const pkgJsonPath = join13(stalePath, "package.json");
15695
+ const pkgJsonPath = join14(stalePath, "package.json");
15527
15696
  if (!existsSync5(pkgJsonPath)) return;
15528
15697
  let pkg = {};
15529
15698
  try {
15530
- pkg = JSON.parse(readFileSync8(pkgJsonPath, "utf8"));
15699
+ pkg = JSON.parse(readFileSync9(pkgJsonPath, "utf8"));
15531
15700
  } catch {
15532
15701
  return;
15533
15702
  }
@@ -15543,10 +15712,10 @@ function cleanupOne(cacheRoot, ourDir) {
15543
15712
  });
15544
15713
  return;
15545
15714
  }
15546
- const cachePkgJson = join13(cacheRoot, "package.json");
15715
+ const cachePkgJson = join14(cacheRoot, "package.json");
15547
15716
  if (!existsSync5(cachePkgJson)) return;
15548
15717
  try {
15549
- const cfg = JSON.parse(readFileSync8(cachePkgJson, "utf8"));
15718
+ const cfg = JSON.parse(readFileSync9(cachePkgJson, "utf8"));
15550
15719
  if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
15551
15720
  delete cfg.dependencies[STALE_PACKAGE_NAME];
15552
15721
  writeFileSync7(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
@@ -15626,7 +15795,7 @@ function registerOpencodeLogSink(client) {
15626
15795
  }
15627
15796
 
15628
15797
  // src/v2-client.ts
15629
- import path13 from "path";
15798
+ import path14 from "path";
15630
15799
  function formatAgentTypeList(agents) {
15631
15800
  const lines = agents.flatMap((agent) => {
15632
15801
  if (agent.hidden === true || agent.mode === "primary") return [];
@@ -15655,7 +15824,7 @@ function toV1Session(info) {
15655
15824
  if (!info || typeof info !== "object") return void 0;
15656
15825
  const session = info;
15657
15826
  const root = session.location?.directory;
15658
- const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path13.resolve(root, session.subpath) : root : void 0;
15827
+ const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path14.resolve(root, session.subpath) : root : void 0;
15659
15828
  return { ...info, directory, parentID: session.parentID };
15660
15829
  }
15661
15830
  function toV1Messages(messages) {
@@ -16190,6 +16359,9 @@ function createClaudeCode(settings = {}) {
16190
16359
  mcpConfig: settings.mcpConfig,
16191
16360
  strictMcpConfig: settings.strictMcpConfig,
16192
16361
  bridgeOpencodeMcp: settings.bridgeOpencodeMcp ?? true,
16362
+ // No `?? true` twin: reading opencode's credential store is opt-in, so
16363
+ // the absent value has to stay falsy.
16364
+ bridgeMcpOauthTokens: settings.bridgeMcpOauthTokens,
16193
16365
  controlRequestBehavior: preset?.controlRequestBehavior ?? settings.controlRequestBehavior ?? "allow",
16194
16366
  controlRequestToolBehaviors: preset ? void 0 : settings.controlRequestToolBehaviors,
16195
16367
  controlRequestDenyMessage: settings.controlRequestDenyMessage,