@khalilgharbaoui/opencode-claude-code-plugin 0.46.0 → 0.47.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
@@ -1490,7 +1490,7 @@ function createAccountFailoverQuestionCall(sessionKey2, input, toolCallId = `${A
1490
1490
  options: [
1491
1491
  ...candidates.map((candidate) => ({
1492
1492
  label: candidate,
1493
- description: `Run on "${candidate}" until ${until}. The conversation is replayed as a fresh Claude session (a session cannot resume across accounts), and any MCP server configured only in "${source}"'s Claude profile will be missing.`
1493
+ description: input.carryTranscript === false ? `Run on "${candidate}" until ${until}. The conversation is replayed as a fresh Claude session, and any MCP server configured only in "${source}"'s Claude profile will be missing.` : `Run on "${candidate}" until ${until}. The conversation continues where it stopped if its Claude transcript can be carried across, and is replayed as a fresh Claude session if it cannot; either way, any MCP server configured only in "${source}"'s Claude profile will be missing.`
1494
1494
  })),
1495
1495
  {
1496
1496
  label: STOP_ANSWER,
@@ -1601,20 +1601,20 @@ function stripAccountFailoverParts(prompt) {
1601
1601
  }
1602
1602
  return changed ? out : prompt;
1603
1603
  }
1604
- function failoverContinuationText(target) {
1604
+ function failoverContinuationText(target, resumed = false) {
1605
1605
  return [
1606
- `The Claude account this conversation was running on hit its usage limit, so it has been moved to the "${target}" account and you are now in a fresh Claude session.`,
1607
- "The conversation so far is above. Continue the task from where it stopped: do not start over, do not re-plan, and do not repeat work that is already done.",
1606
+ resumed ? `The Claude account this conversation was running on hit its usage limit, so it has been moved to the "${target}" account. This is the same conversation, continued: everything you already know still stands.` : `The Claude account this conversation was running on hit its usage limit, so it has been moved to the "${target}" account and you are now in a fresh Claude session. The conversation so far is above.`,
1607
+ "Continue the task from where it stopped: do not start over, do not re-plan, and do not repeat work that is already done.",
1608
1608
  "Do not mention the account switch unless you are asked about it."
1609
1609
  ].join(" ");
1610
1610
  }
1611
- function buildFailoverContinuationPrompt(prompt, target) {
1611
+ function buildFailoverContinuationPrompt(prompt, target, resumed = false) {
1612
1612
  const stripped = stripAccountFailoverParts(prompt);
1613
1613
  return [
1614
1614
  ...stripped,
1615
1615
  {
1616
1616
  role: "user",
1617
- content: [{ type: "text", text: failoverContinuationText(target) }]
1617
+ content: [{ type: "text", text: failoverContinuationText(target, resumed) }]
1618
1618
  }
1619
1619
  ];
1620
1620
  }
@@ -1625,7 +1625,108 @@ function failoverUntil(resetsAt) {
1625
1625
  // src/cli-version.ts
1626
1626
  import { execFile } from "child_process";
1627
1627
  import { promisify } from "util";
1628
+
1629
+ // src/windows-spawn.ts
1630
+ import * as fs from "fs";
1631
+ import * as path2 from "path";
1632
+ var DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
1633
+ function envValue(env, name) {
1634
+ const direct = env[name];
1635
+ if (direct !== void 0) return direct;
1636
+ const match = Object.keys(env).find((key) => key.toLowerCase() === name.toLowerCase());
1637
+ return match === void 0 ? void 0 : env[match];
1638
+ }
1639
+ function defaultIsFile(candidate) {
1640
+ try {
1641
+ return fs.statSync(candidate).isFile();
1642
+ } catch {
1643
+ return false;
1644
+ }
1645
+ }
1646
+ function executableExtensions(env = process.env) {
1647
+ const raw = envValue(env, "PATHEXT") ?? DEFAULT_PATHEXT;
1648
+ return raw.split(";").map((ext) => ext.trim().toLowerCase()).filter(Boolean).map((ext) => ext.startsWith(".") ? ext : `.${ext}`);
1649
+ }
1650
+ function isBatchFile(file) {
1651
+ const ext = path2.win32.extname(file).toLowerCase();
1652
+ return ext === ".cmd" || ext === ".bat";
1653
+ }
1654
+ function resolveWindowsCommand(command, deps = {}) {
1655
+ const env = deps.env ?? process.env;
1656
+ const isFile = deps.isFile ?? defaultIsFile;
1657
+ const extensions = executableExtensions(env);
1658
+ const candidatesFor = (base) => {
1659
+ const ext = path2.win32.extname(base).toLowerCase();
1660
+ if (ext && extensions.includes(ext)) return [base];
1661
+ return [...extensions.map((candidate) => base + candidate), base];
1662
+ };
1663
+ if (command.includes("\\") || command.includes("/")) {
1664
+ for (const candidate of candidatesFor(command)) {
1665
+ if (isFile(candidate)) return candidate;
1666
+ }
1667
+ return null;
1668
+ }
1669
+ const pathValue = envValue(env, "PATH") ?? "";
1670
+ for (const entry of pathValue.split(path2.win32.delimiter)) {
1671
+ const directory = entry.trim().replace(/^"(.*)"$/, "$1");
1672
+ if (!directory) continue;
1673
+ for (const candidate of candidatesFor(path2.win32.join(directory, command))) {
1674
+ if (isFile(candidate)) return candidate;
1675
+ }
1676
+ }
1677
+ return null;
1678
+ }
1679
+ function quoteBatchArgument(arg) {
1680
+ if (/[\r\n]/.test(arg)) {
1681
+ throw new Error("cmd.exe cannot carry a newline in an argument");
1682
+ }
1683
+ let out = '"';
1684
+ let backslashes = 0;
1685
+ for (const char of arg) {
1686
+ if (char === "\\") {
1687
+ backslashes += 1;
1688
+ continue;
1689
+ }
1690
+ if (char === '"') {
1691
+ out += "\\".repeat(backslashes * 2) + '""';
1692
+ backslashes = 0;
1693
+ continue;
1694
+ }
1695
+ out += "\\".repeat(backslashes) + char;
1696
+ backslashes = 0;
1697
+ }
1698
+ return `${out}${"\\".repeat(backslashes * 2)}"`;
1699
+ }
1700
+ function planCmdInvocation(file, args, env = process.env) {
1701
+ const comspec = envValue(env, "ComSpec") ?? "cmd.exe";
1702
+ const line = [`"${path2.win32.normalize(file)}"`, ...args.map(quoteBatchArgument)];
1703
+ return {
1704
+ file: comspec,
1705
+ args: ["/d", "/s", "/c", `"${line.join(" ")}"`],
1706
+ windowsVerbatimArguments: true
1707
+ };
1708
+ }
1709
+ function planClaudeSpawn(command, args, deps = {}) {
1710
+ const platform2 = deps.platform ?? process.platform;
1711
+ if (platform2 !== "win32") return { file: command, args: [...args] };
1712
+ const env = deps.env ?? process.env;
1713
+ const resolved = resolveWindowsCommand(command, deps) ?? command;
1714
+ if (isBatchFile(resolved)) return planCmdInvocation(resolved, args, env);
1715
+ return { file: resolved, args: [...args] };
1716
+ }
1717
+
1718
+ // src/cli-version.ts
1628
1719
  var execFileAsync = promisify(execFile);
1720
+ function probeExec(cliPath, args, options) {
1721
+ const plan = planClaudeSpawn(cliPath, args);
1722
+ return execFileAsync(plan.file, plan.args, {
1723
+ ...options,
1724
+ // utf8 is already `execFile`'s default; naming it is what keeps the
1725
+ // promisified overload returning strings rather than Buffers.
1726
+ encoding: "utf8",
1727
+ windowsVerbatimArguments: plan.windowsVerbatimArguments
1728
+ });
1729
+ }
1629
1730
  var cache = /* @__PURE__ */ new Map();
1630
1731
  var PROBE_TIMEOUT_MS = 5e3;
1631
1732
  var probeTimeoutMs = PROBE_TIMEOUT_MS;
@@ -1672,7 +1773,7 @@ function detectCliVersion(cliPath) {
1672
1773
  let deadlineKill = false;
1673
1774
  const promise = (async () => {
1674
1775
  try {
1675
- const { stdout } = await execFileAsync(cliPath, ["--version"], {
1776
+ const { stdout } = await probeExec(cliPath, ["--version"], {
1676
1777
  timeout: probeTimeoutMs
1677
1778
  });
1678
1779
  const match = /(\d+)\.(\d+)\.(\d+)/.exec(stdout.trim());
@@ -1769,7 +1870,7 @@ function detectHeadlessSupport(cliPath) {
1769
1870
  let deadlineKill = false;
1770
1871
  const promise = (async () => {
1771
1872
  try {
1772
- const execution = execFileAsync(cliPath, [
1873
+ const execution = probeExec(cliPath, [
1773
1874
  "--print",
1774
1875
  "--input-format",
1775
1876
  "stream-json",
@@ -1805,7 +1906,7 @@ function detectCliSupportsFlag(cliPath, flag) {
1805
1906
  let deadlineKill = false;
1806
1907
  const promise = (async () => {
1807
1908
  try {
1808
- const execution = execFileAsync(cliPath, ["--help"], {
1909
+ const execution = probeExec(cliPath, ["--help"], {
1809
1910
  timeout: probeTimeoutMs,
1810
1911
  killSignal: "SIGKILL",
1811
1912
  maxBuffer: 4 * 1024 * 1024
@@ -1838,17 +1939,17 @@ import { unlink } from "fs/promises";
1838
1939
 
1839
1940
  // src/proxy-mcp.ts
1840
1941
  import { createServer } from "http";
1841
- import * as fs2 from "fs";
1842
- import * as path3 from "path";
1942
+ import * as fs3 from "fs";
1943
+ import * as path4 from "path";
1843
1944
  import * as crypto from "crypto";
1844
1945
  import { EventEmitter } from "events";
1845
1946
 
1846
1947
  // src/tmp.ts
1847
- import * as fs from "fs";
1948
+ import * as fs2 from "fs";
1848
1949
  import * as os from "os";
1849
- import * as path2 from "path";
1950
+ import * as path3 from "path";
1850
1951
  var PLUGIN_TMP_PREFIX = "opencode-claude-code-";
1851
- var PID_TMP_DIR = path2.join(
1952
+ var PID_TMP_DIR = path3.join(
1852
1953
  os.tmpdir(),
1853
1954
  `${PLUGIN_TMP_PREFIX}${process.pid}`
1854
1955
  );
@@ -1861,7 +1962,7 @@ function currentUid() {
1861
1962
  function untrustworthyReason(dir) {
1862
1963
  let stat;
1863
1964
  try {
1864
- stat = fs.lstatSync(dir);
1965
+ stat = fs2.lstatSync(dir);
1865
1966
  } catch {
1866
1967
  return null;
1867
1968
  }
@@ -1874,16 +1975,16 @@ function untrustworthyReason(dir) {
1874
1975
  return null;
1875
1976
  }
1876
1977
  function ensureDir(dir) {
1877
- fs.mkdirSync(dir, { recursive: true, mode: 448 });
1978
+ fs2.mkdirSync(dir, { recursive: true, mode: 448 });
1878
1979
  try {
1879
- fs.chmodSync(dir, 448);
1980
+ fs2.chmodSync(dir, 448);
1880
1981
  } catch {
1881
1982
  }
1882
1983
  }
1883
1984
  function chooseDir() {
1884
1985
  const reason = untrustworthyReason(PID_TMP_DIR);
1885
1986
  if (!reason) return PID_TMP_DIR;
1886
- const fallback = fs.mkdtempSync(path2.join(os.tmpdir(), PLUGIN_TMP_PREFIX));
1987
+ const fallback = fs2.mkdtempSync(path3.join(os.tmpdir(), PLUGIN_TMP_PREFIX));
1887
1988
  log.warn("refusing the pid-named scratch directory", {
1888
1989
  path: PID_TMP_DIR,
1889
1990
  reason,
@@ -1897,7 +1998,7 @@ function registerExitCleanup() {
1897
1998
  process.on("exit", () => {
1898
1999
  for (const dir of createdDirs) {
1899
2000
  try {
1900
- fs.rmSync(dir, { recursive: true, force: true });
2001
+ fs2.rmSync(dir, { recursive: true, force: true });
1901
2002
  } catch {
1902
2003
  }
1903
2004
  }
@@ -1918,23 +2019,23 @@ function sweepStalePluginTmpDirs(keep) {
1918
2019
  const root = os.tmpdir();
1919
2020
  let entries;
1920
2021
  try {
1921
- entries = fs.readdirSync(root);
2022
+ entries = fs2.readdirSync(root);
1922
2023
  } catch {
1923
2024
  return removed;
1924
2025
  }
1925
2026
  const uid = currentUid();
1926
- const keepPath = keep ? path2.resolve(keep) : null;
2027
+ const keepPath = keep ? path3.resolve(keep) : null;
1927
2028
  for (const entry of entries) {
1928
2029
  const match = PID_DIR_NAME.exec(entry);
1929
2030
  if (!match) continue;
1930
2031
  const pid = Number(match[1]);
1931
2032
  if (!Number.isSafeInteger(pid) || pid <= 0) continue;
1932
2033
  if (pid === process.pid) continue;
1933
- const dir = path2.join(root, entry);
1934
- if (keepPath && path2.resolve(dir) === keepPath) continue;
2034
+ const dir = path3.join(root, entry);
2035
+ if (keepPath && path3.resolve(dir) === keepPath) continue;
1935
2036
  let stat;
1936
2037
  try {
1937
- stat = fs.lstatSync(dir);
2038
+ stat = fs2.lstatSync(dir);
1938
2039
  } catch {
1939
2040
  continue;
1940
2041
  }
@@ -1942,7 +2043,7 @@ function sweepStalePluginTmpDirs(keep) {
1942
2043
  if (uid !== void 0 && stat.uid !== uid) continue;
1943
2044
  if (isProcessAlive(pid)) continue;
1944
2045
  try {
1945
- fs.rmSync(dir, { recursive: true, force: true });
2046
+ fs2.rmSync(dir, { recursive: true, force: true });
1946
2047
  removed.push(dir);
1947
2048
  } catch (err) {
1948
2049
  log.debug("could not remove a stale scratch directory", {
@@ -2773,11 +2874,11 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
2773
2874
  2
2774
2875
  );
2775
2876
  const hash = crypto.createHash("sha256").update(body).digest("hex").slice(0, 12);
2776
- const outPath = path3.join(
2877
+ const outPath = path4.join(
2777
2878
  pluginTmpDir(),
2778
2879
  `proxy-${hash}.json`
2779
2880
  );
2780
- fs2.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
2881
+ fs3.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
2781
2882
  configFilePath = outPath;
2782
2883
  return outPath;
2783
2884
  },
@@ -2794,7 +2895,7 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
2794
2895
  });
2795
2896
  if (configFilePath) {
2796
2897
  try {
2797
- fs2.unlinkSync(configFilePath);
2898
+ fs3.unlinkSync(configFilePath);
2798
2899
  } catch {
2799
2900
  }
2800
2901
  configFilePath = null;
@@ -3363,8 +3464,8 @@ function clearCompression(sessionKey2) {
3363
3464
  }
3364
3465
 
3365
3466
  // src/mcp-bridge.ts
3366
- import * as fs3 from "fs";
3367
- import * as path4 from "path";
3467
+ import * as fs4 from "fs";
3468
+ import * as path5 from "path";
3368
3469
  import * as os2 from "os";
3369
3470
  import * as crypto2 from "crypto";
3370
3471
  import {
@@ -3375,21 +3476,21 @@ var FILE_NAMES = ["opencode.jsonc", "opencode.json", "config.json"];
3375
3476
  var PROJECT_FILE_NAMES = ["opencode.json", "opencode.jsonc"];
3376
3477
  function fileExists(p) {
3377
3478
  try {
3378
- return fs3.statSync(p).isFile();
3479
+ return fs4.statSync(p).isFile();
3379
3480
  } catch {
3380
3481
  return false;
3381
3482
  }
3382
3483
  }
3383
3484
  function dirExists(p) {
3384
3485
  try {
3385
- return fs3.statSync(p).isDirectory();
3486
+ return fs4.statSync(p).isDirectory();
3386
3487
  } catch {
3387
3488
  return false;
3388
3489
  }
3389
3490
  }
3390
3491
  function readAndParse(file) {
3391
3492
  try {
3392
- const raw = fs3.readFileSync(file, "utf8");
3493
+ const raw = fs4.readFileSync(file, "utf8");
3393
3494
  const errors = [];
3394
3495
  const parsed = parseJsonc(raw, errors, { allowTrailingComma: true });
3395
3496
  if (errors.length > 0) {
@@ -3425,14 +3526,14 @@ function deepMerge(target, source) {
3425
3526
  }
3426
3527
  function walkUp(opts) {
3427
3528
  const out = [];
3428
- let current = path4.resolve(opts.start);
3529
+ let current = path5.resolve(opts.start);
3429
3530
  while (true) {
3430
3531
  for (const target of opts.targets) {
3431
- const candidate = path4.join(current, target);
3532
+ const candidate = path5.join(current, target);
3432
3533
  if (opts.predicate(candidate)) out.push(candidate);
3433
3534
  }
3434
- if (opts.stop && current === path4.resolve(opts.stop)) break;
3435
- const parent = path4.dirname(current);
3535
+ if (opts.stop && current === path5.resolve(opts.stop)) break;
3536
+ const parent = path5.dirname(current);
3436
3537
  if (parent === current) break;
3437
3538
  current = parent;
3438
3539
  }
@@ -3440,28 +3541,28 @@ function walkUp(opts) {
3440
3541
  }
3441
3542
  function detectWorktree(cwd) {
3442
3543
  const override = process.env.OPENCODE_WORKTREE;
3443
- if (override) return path4.resolve(override);
3444
- let current = path4.resolve(cwd);
3544
+ if (override) return path5.resolve(override);
3545
+ let current = path5.resolve(cwd);
3445
3546
  while (true) {
3446
- const gitPath = path4.join(current, ".git");
3547
+ const gitPath = path5.join(current, ".git");
3447
3548
  try {
3448
- if (fs3.existsSync(gitPath)) return current;
3549
+ if (fs4.existsSync(gitPath)) return current;
3449
3550
  } catch {
3450
3551
  }
3451
- const parent = path4.dirname(current);
3552
+ const parent = path5.dirname(current);
3452
3553
  if (parent === current) return void 0;
3453
3554
  current = parent;
3454
3555
  }
3455
3556
  }
3456
3557
  function globalConfigDir() {
3457
- const xdg = process.env.XDG_CONFIG_HOME ?? path4.join(os2.homedir(), ".config");
3458
- return path4.join(xdg, "opencode");
3558
+ const xdg = process.env.XDG_CONFIG_HOME ?? path5.join(os2.homedir(), ".config");
3559
+ return path5.join(xdg, "opencode");
3459
3560
  }
3460
3561
  function loadGlobalConfig(hostApi = "v1") {
3461
3562
  const dir = globalConfigDir();
3462
3563
  let merged = {};
3463
3564
  for (const name of FILE_NAMES.slice().reverse()) {
3464
- const file = path4.join(dir, name);
3565
+ const file = path5.join(dir, name);
3465
3566
  if (!fileExists(file)) continue;
3466
3567
  const parsed = readAndParse(file);
3467
3568
  if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
@@ -3471,7 +3572,7 @@ function loadGlobalConfig(hostApi = "v1") {
3471
3572
  function loadProjectFilesInDir(dir, hostApi = "v1") {
3472
3573
  let merged = {};
3473
3574
  for (const name of PROJECT_FILE_NAMES) {
3474
- const file = path4.join(dir, name);
3575
+ const file = path5.join(dir, name);
3475
3576
  if (!fileExists(file)) continue;
3476
3577
  const parsed = readAndParse(file);
3477
3578
  if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
@@ -3482,7 +3583,7 @@ function dotOpencodeDirs(cwd, worktree) {
3482
3583
  const dirs = [];
3483
3584
  const seen = /* @__PURE__ */ new Set();
3484
3585
  const push = (p) => {
3485
- const abs = path4.resolve(p);
3586
+ const abs = path5.resolve(p);
3486
3587
  if (!seen.has(abs) && dirExists(abs)) {
3487
3588
  seen.add(abs);
3488
3589
  dirs.push(abs);
@@ -3498,7 +3599,7 @@ function dotOpencodeDirs(cwd, worktree) {
3498
3599
  }
3499
3600
  const home = os2.homedir();
3500
3601
  if (home) {
3501
- const homeDot = path4.join(home, ".opencode");
3602
+ const homeDot = path5.join(home, ".opencode");
3502
3603
  if (dirExists(homeDot)) push(homeDot);
3503
3604
  }
3504
3605
  const envDir = process.env.OPENCODE_CONFIG_DIR;
@@ -3645,7 +3746,7 @@ function opencodeConfigLayers(cwd, hostApi = "v1") {
3645
3746
  const projectDirs = [];
3646
3747
  const seenProjectDirs = /* @__PURE__ */ new Set();
3647
3748
  for (const f of projectFiles) {
3648
- const d = path4.dirname(f);
3749
+ const d = path5.dirname(f);
3649
3750
  if (!seenProjectDirs.has(d)) {
3650
3751
  seenProjectDirs.add(d);
3651
3752
  projectDirs.push(d);
@@ -3712,13 +3813,13 @@ function finishBridge(input) {
3712
3813
  }
3713
3814
  const body = JSON.stringify({ mcpServers: servers }, null, 2);
3714
3815
  const bodyDigest = crypto2.createHash("sha256").update(body).digest("hex").slice(0, 12);
3715
- const outPath = path4.join(
3816
+ const outPath = path5.join(
3716
3817
  pluginTmpDir(),
3717
3818
  `mcp-${bodyDigest}.json`
3718
3819
  );
3719
3820
  try {
3720
3821
  if (!fileExists(outPath)) {
3721
- fs3.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
3822
+ fs4.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
3722
3823
  }
3723
3824
  } catch (e) {
3724
3825
  log.warn("failed to write bridged MCP config", {
@@ -5308,11 +5409,12 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
5308
5409
  effort,
5309
5410
  promptCacheTtl
5310
5411
  });
5311
- const proc = spawn(cliPath, cliArgs, {
5412
+ const plan = planClaudeSpawn(cliPath, cliArgs);
5413
+ const proc = spawn(plan.file, plan.args, {
5312
5414
  cwd,
5313
5415
  stdio: ["pipe", "pipe", "pipe"],
5314
5416
  env: claudeSpawnEnv({ ignoreAnthropicApiKey, effort, promptCacheTtl }),
5315
- shell: process.platform === "win32"
5417
+ windowsVerbatimArguments: plan.windowsVerbatimArguments
5316
5418
  });
5317
5419
  const lineEmitter = new EventEmitter3();
5318
5420
  const ap = {
@@ -5892,6 +5994,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
5892
5994
  "btw: a turn is running but no claude process appeared for it",
5893
5995
  "btw: early aside failed; the message will ask again",
5894
5996
  "cannot interrupt this transport; waiting for the turn to end",
5997
+ "carried this conversation's claude transcript to the other account",
5895
5998
  "carrying the claude conversation over to the new session key",
5896
5999
  "claude --version output unparseable",
5897
6000
  "claude account is out of usage; the turn ends with a note",
@@ -5989,6 +6092,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
5989
6092
  "remote URL images are not supported by Claude CLI, skipping",
5990
6093
  "replaying stdout the child emitted between turns",
5991
6094
  "replaying the conversation as text: no claude session was available to continue",
6095
+ "replaying this conversation as text on the other account",
5992
6096
  "result fallback timer fired \u2014 closing stream without result event",
5993
6097
  "resuming the claude session from before the restart instead of replaying it",
5994
6098
  "shared Claude capability already exists; leaving untouched",
@@ -6033,6 +6137,8 @@ var BUNDLE_DATA_ALLOWLIST = {
6033
6137
  emitted: "flag",
6034
6138
  enabled: "flag",
6035
6139
  executed: "flag",
6140
+ failedOver: "flag",
6141
+ renamed: "flag",
6036
6142
  isUsingOverage: "flag",
6037
6143
  hadProxyActivity: "flag",
6038
6144
  hadReasoning: "flag",
@@ -6411,8 +6517,8 @@ function redactLogLine(raw, context) {
6411
6517
  if (!parsed.data || Object.keys(parsed.data).length === 0) return head;
6412
6518
  return `${head} ${JSON.stringify(sanitizeLogData(parsed.data, context))}`;
6413
6519
  }
6414
- function readLogTail(path11, maxBytes = BUNDLE_TAIL_BYTES) {
6415
- const fd = openSync(path11, "r");
6520
+ function readLogTail(path13, maxBytes = BUNDLE_TAIL_BYTES) {
6521
+ const fd = openSync(path13, "r");
6416
6522
  try {
6417
6523
  const size = fstatSync(fd).size;
6418
6524
  const length = Math.min(size, maxBytes);
@@ -6562,13 +6668,20 @@ function parsePlanUsage(stdout) {
6562
6668
  return { status: "failed", error: "no result object in the CLI's reply" };
6563
6669
  }
6564
6670
  function runCli(cliPath, args, timeoutMs, env) {
6671
+ const plan = planClaudeSpawn(cliPath, args);
6565
6672
  return new Promise((resolve6, reject) => {
6566
6673
  execFile2(
6567
- cliPath,
6568
- args,
6674
+ plan.file,
6675
+ plan.args,
6569
6676
  // `killSignal` so a CLI wedged on a hook is actually gone, and a generous
6570
6677
  // buffer because the reply is prose of unbounded length.
6571
- { timeout: timeoutMs, killSignal: "SIGKILL", maxBuffer: 4 * 1024 * 1024, env },
6678
+ {
6679
+ timeout: timeoutMs,
6680
+ killSignal: "SIGKILL",
6681
+ maxBuffer: 4 * 1024 * 1024,
6682
+ env,
6683
+ windowsVerbatimArguments: plan.windowsVerbatimArguments
6684
+ },
6572
6685
  (error, stdout) => {
6573
6686
  if (stdout && stdout.trim()) return resolve6(stdout);
6574
6687
  if (error) return reject(error);
@@ -6607,14 +6720,14 @@ async function fetchPlanUsage(cliPath, options = {}) {
6607
6720
  }
6608
6721
 
6609
6722
  // src/stale-build.ts
6610
- import * as fs5 from "fs";
6611
- import * as path6 from "path";
6723
+ import * as fs6 from "fs";
6724
+ import * as path7 from "path";
6612
6725
  import { fileURLToPath as fileURLToPath2 } from "url";
6613
6726
 
6614
6727
  // src/startup-diagnostics.ts
6615
6728
  import { execFile as execFile3 } from "child_process";
6616
- import * as fs4 from "fs";
6617
- import * as path5 from "path";
6729
+ import * as fs5 from "fs";
6730
+ import * as path6 from "path";
6618
6731
  import { promisify as promisify2 } from "util";
6619
6732
  import { fileURLToPath } from "url";
6620
6733
 
@@ -6649,8 +6762,8 @@ var cachedPluginVersion;
6649
6762
  function pluginVersion() {
6650
6763
  if (cachedPluginVersion) return cachedPluginVersion;
6651
6764
  try {
6652
- const here = path5.dirname(fileURLToPath(import.meta.url));
6653
- const raw = fs4.readFileSync(path5.join(here, "..", "package.json"), "utf8");
6765
+ const here = path6.dirname(fileURLToPath(import.meta.url));
6766
+ const raw = fs5.readFileSync(path6.join(here, "..", "package.json"), "utf8");
6654
6767
  const version = JSON.parse(raw).version;
6655
6768
  cachedPluginVersion = typeof version === "string" ? version : "unknown";
6656
6769
  } catch {
@@ -6674,7 +6787,7 @@ var opencodeVersionProbe;
6674
6787
  function detectOpencodeVersion(execPath = process.execPath) {
6675
6788
  if (opencodeVersionProbe) return opencodeVersionProbe;
6676
6789
  opencodeVersionProbe = (async () => {
6677
- if (!path5.basename(execPath).toLowerCase().includes("opencode")) {
6790
+ if (!path6.basename(execPath).toLowerCase().includes("opencode")) {
6678
6791
  log.debug("skipping opencode version probe: execPath is not opencode", { execPath });
6679
6792
  return void 0;
6680
6793
  }
@@ -6903,10 +7016,10 @@ function defaultEntryPath() {
6903
7016
  }
6904
7017
  function readOnDiskBuild(entryPath) {
6905
7018
  try {
6906
- const raw = fs5.readFileSync(path6.join(path6.dirname(entryPath), "..", "package.json"), "utf8");
7019
+ const raw = fs6.readFileSync(path7.join(path7.dirname(entryPath), "..", "package.json"), "utf8");
6907
7020
  const version = JSON.parse(raw).version;
6908
7021
  if (typeof version !== "string" || version.length === 0) return void 0;
6909
- const stat = fs5.statSync(entryPath);
7022
+ const stat = fs6.statSync(entryPath);
6910
7023
  return { version, mtimeMs: stat.mtimeMs, size: stat.size };
6911
7024
  } catch {
6912
7025
  return void 0;
@@ -6917,7 +7030,7 @@ function recordLoadedBuild() {
6917
7030
  let mtimeMs;
6918
7031
  let size;
6919
7032
  try {
6920
- const stat = fs5.statSync(entryPath);
7033
+ const stat = fs6.statSync(entryPath);
6921
7034
  mtimeMs = stat.mtimeMs;
6922
7035
  size = stat.size;
6923
7036
  } catch {
@@ -7354,7 +7467,7 @@ Could not build the report: ${message}`;
7354
7467
 
7355
7468
  // src/agent-models.ts
7356
7469
  import { readFile, readdir } from "fs/promises";
7357
- import path7 from "path";
7470
+ import path8 from "path";
7358
7471
 
7359
7472
  // src/models.ts
7360
7473
  var PROVIDER_ID = "claude-code";
@@ -7878,13 +7991,13 @@ async function readAgentMarkdownRecords(directories) {
7878
7991
  const name = entry.slice(0, -3);
7879
7992
  if (records2[name]) continue;
7880
7993
  try {
7881
- const text = await readFile(path7.join(directory, entry), "utf8");
7994
+ const text = await readFile(path8.join(directory, entry), "utf8");
7882
7995
  records2[name] = parseAgentFrontmatter(text);
7883
7996
  const prompt = agentMarkdownBody(text);
7884
7997
  if (prompt) records2[name].prompt = prompt;
7885
7998
  } catch (err) {
7886
7999
  log.debug("failed to read agent markdown", {
7887
- file: path7.join(directory, entry),
8000
+ file: path8.join(directory, entry),
7888
8001
  error: String(err)
7889
8002
  });
7890
8003
  }
@@ -7896,12 +8009,12 @@ function agentDirectories(home, projectDirectory) {
7896
8009
  const directories = [];
7897
8010
  if (projectDirectory) {
7898
8011
  for (const name of AGENT_DIR_NAMES) {
7899
- directories.push(path7.join(projectDirectory, ".opencode", name));
8012
+ directories.push(path8.join(projectDirectory, ".opencode", name));
7900
8013
  }
7901
8014
  }
7902
8015
  if (home) {
7903
8016
  for (const name of AGENT_DIR_NAMES) {
7904
- directories.push(path7.join(home, ".config", "opencode", name));
8017
+ directories.push(path8.join(home, ".config", "opencode", name));
7905
8018
  }
7906
8019
  }
7907
8020
  return directories;
@@ -8501,14 +8614,14 @@ function getTrailingUserMessages(prompt, opts = {}) {
8501
8614
 
8502
8615
  // src/skill-bridge.ts
8503
8616
  import * as crypto3 from "crypto";
8504
- import * as fs6 from "fs";
8617
+ import * as fs7 from "fs";
8505
8618
  import * as os3 from "os";
8506
- import * as path8 from "path";
8619
+ import * as path9 from "path";
8507
8620
  import { fileURLToPath as fileURLToPath3 } from "url";
8508
8621
  function bundledSkillsDir() {
8509
8622
  try {
8510
8623
  const here = fileURLToPath3(import.meta.url);
8511
- const dir = path8.resolve(path8.dirname(here), "..", "skills");
8624
+ const dir = path9.resolve(path9.dirname(here), "..", "skills");
8512
8625
  return dirExists2(dir) ? dir : null;
8513
8626
  } catch {
8514
8627
  return null;
@@ -8516,16 +8629,16 @@ function bundledSkillsDir() {
8516
8629
  }
8517
8630
  function dirExists2(p) {
8518
8631
  try {
8519
- return fs6.statSync(p).isDirectory();
8632
+ return fs7.statSync(p).isDirectory();
8520
8633
  } catch {
8521
8634
  return false;
8522
8635
  }
8523
8636
  }
8524
8637
  function realPath(p) {
8525
8638
  try {
8526
- return fs6.realpathSync(p);
8639
+ return fs7.realpathSync(p);
8527
8640
  } catch {
8528
- return path8.resolve(p);
8641
+ return path9.resolve(p);
8529
8642
  }
8530
8643
  }
8531
8644
  function envEnabled(name) {
@@ -8546,10 +8659,10 @@ function declaredSkillName(source) {
8546
8659
  return SAFE_SKILL_NAME.test(name) ? name : null;
8547
8660
  }
8548
8661
  function readSkill(root, entry) {
8549
- const dir = path8.join(root, entry);
8662
+ const dir = path9.join(root, entry);
8550
8663
  let source;
8551
8664
  try {
8552
- source = fs6.readFileSync(path8.join(dir, "SKILL.md"), "utf8");
8665
+ source = fs7.readFileSync(path9.join(dir, "SKILL.md"), "utf8");
8553
8666
  } catch {
8554
8667
  return null;
8555
8668
  }
@@ -8567,47 +8680,47 @@ function skillRoots(cwd) {
8567
8680
  const roots = [];
8568
8681
  const seen = /* @__PURE__ */ new Set();
8569
8682
  const push = (p) => {
8570
- const abs = path8.resolve(p);
8683
+ const abs = path9.resolve(p);
8571
8684
  if (seen.has(abs)) return;
8572
8685
  seen.add(abs);
8573
8686
  if (dirExists2(abs)) roots.push(abs);
8574
8687
  };
8575
8688
  const home = os3.homedir();
8576
- const homeDir = home ? path8.resolve(home) : null;
8577
- let current = path8.resolve(cwd);
8689
+ const homeDir = home ? path9.resolve(home) : null;
8690
+ let current = path9.resolve(cwd);
8578
8691
  while (true) {
8579
- push(path8.join(current, ".opencode", "skills"));
8692
+ push(path9.join(current, ".opencode", "skills"));
8580
8693
  if (current !== homeDir) {
8581
- push(path8.join(current, ".claude", "skills"));
8582
- push(path8.join(current, ".agents", "skills"));
8694
+ push(path9.join(current, ".claude", "skills"));
8695
+ push(path9.join(current, ".agents", "skills"));
8583
8696
  }
8584
- const parent = path8.dirname(current);
8697
+ const parent = path9.dirname(current);
8585
8698
  if (parent === current) break;
8586
8699
  current = parent;
8587
8700
  }
8588
- if (homeDir) push(path8.join(homeDir, ".opencode", "skills"));
8701
+ if (homeDir) push(path9.join(homeDir, ".opencode", "skills"));
8589
8702
  const envDir = process.env.OPENCODE_CONFIG_DIR;
8590
8703
  if (envDir) {
8591
- push(path8.join(envDir, "skills"));
8592
- push(path8.join(envDir, "skill"));
8704
+ push(path9.join(envDir, "skills"));
8705
+ push(path9.join(envDir, "skill"));
8593
8706
  }
8594
- const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path8.join(homeDir, ".config") : null);
8707
+ const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path9.join(homeDir, ".config") : null);
8595
8708
  if (xdg) {
8596
- push(path8.join(xdg, "opencode", "skills"));
8597
- push(path8.join(xdg, "opencode", "skill"));
8709
+ push(path9.join(xdg, "opencode", "skills"));
8710
+ push(path9.join(xdg, "opencode", "skill"));
8598
8711
  }
8599
8712
  if (homeDir && !envEnabled("OPENCODE_DISABLE_EXTERNAL_SKILLS")) {
8600
8713
  if (!envEnabled("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS")) {
8601
- push(path8.join(homeDir, ".claude", "skills"));
8714
+ push(path9.join(homeDir, ".claude", "skills"));
8602
8715
  }
8603
- push(path8.join(homeDir, ".agents", "skills"));
8716
+ push(path9.join(homeDir, ".agents", "skills"));
8604
8717
  }
8605
8718
  return roots;
8606
8719
  }
8607
8720
  function collectSkills(root, claimed, found) {
8608
8721
  let entries;
8609
8722
  try {
8610
- entries = fs6.readdirSync(root, { withFileTypes: true });
8723
+ entries = fs7.readdirSync(root, { withFileTypes: true });
8611
8724
  } catch {
8612
8725
  return;
8613
8726
  }
@@ -8647,23 +8760,23 @@ function discoverBundledSkills() {
8647
8760
  }
8648
8761
  function claudeConfigDir(configDir) {
8649
8762
  const explicit = configDir ?? process.env.CLAUDE_CONFIG_DIR;
8650
- if (explicit) return path8.resolve(expandHome(explicit));
8763
+ if (explicit) return path9.resolve(expandHome(explicit));
8651
8764
  const home = os3.homedir();
8652
- return home ? path8.resolve(home, ".claude") : null;
8765
+ return home ? path9.resolve(home, ".claude") : null;
8653
8766
  }
8654
8767
  function installedPluginSkillRoots(configDir, cwd) {
8655
8768
  if (!configDir) return [];
8656
8769
  let parsed;
8657
8770
  try {
8658
8771
  parsed = JSON.parse(
8659
- fs6.readFileSync(path8.join(configDir, "plugins", "installed_plugins.json"), "utf8")
8772
+ fs7.readFileSync(path9.join(configDir, "plugins", "installed_plugins.json"), "utf8")
8660
8773
  );
8661
8774
  } catch {
8662
8775
  return [];
8663
8776
  }
8664
8777
  const plugins = parsed?.plugins;
8665
8778
  if (!plugins || typeof plugins !== "object") return [];
8666
- const workspace = path8.resolve(cwd);
8779
+ const workspace = path9.resolve(cwd);
8667
8780
  const roots = [];
8668
8781
  for (const installs of Object.values(plugins)) {
8669
8782
  if (!Array.isArray(installs)) continue;
@@ -8671,28 +8784,28 @@ function installedPluginSkillRoots(configDir, cwd) {
8671
8784
  const entry = install;
8672
8785
  if (typeof entry?.installPath !== "string" || !entry.installPath) continue;
8673
8786
  if (entry.scope !== "user" && !coversWorkspace(entry.projectPath, workspace)) continue;
8674
- roots.push(path8.join(entry.installPath, "skills"));
8787
+ roots.push(path9.join(entry.installPath, "skills"));
8675
8788
  }
8676
8789
  }
8677
8790
  return roots;
8678
8791
  }
8679
8792
  function coversWorkspace(projectPath, workspace) {
8680
8793
  if (typeof projectPath !== "string" || !projectPath) return false;
8681
- const root = path8.resolve(projectPath);
8794
+ const root = path9.resolve(projectPath);
8682
8795
  if (root === workspace) return true;
8683
- const rel = path8.relative(root, workspace);
8684
- return rel !== "" && !rel.startsWith("..") && !path8.isAbsolute(rel);
8796
+ const rel = path9.relative(root, workspace);
8797
+ return rel !== "" && !rel.startsWith("..") && !path9.isAbsolute(rel);
8685
8798
  }
8686
8799
  function discoverNativeClaudeSkills(opts) {
8687
8800
  const found = [];
8688
8801
  const scanned = /* @__PURE__ */ new Set();
8689
8802
  const scan = (root, scope) => {
8690
- const abs = path8.resolve(root);
8803
+ const abs = path9.resolve(root);
8691
8804
  if (scanned.has(abs)) return;
8692
8805
  scanned.add(abs);
8693
8806
  let entries;
8694
8807
  try {
8695
- entries = fs6.readdirSync(abs, { withFileTypes: true });
8808
+ entries = fs7.readdirSync(abs, { withFileTypes: true });
8696
8809
  } catch {
8697
8810
  return;
8698
8811
  }
@@ -8704,13 +8817,13 @@ function discoverNativeClaudeSkills(opts) {
8704
8817
  }
8705
8818
  };
8706
8819
  const configDir = claudeConfigDir(opts.configDir);
8707
- if (configDir) scan(path8.join(configDir, "skills"), "user");
8820
+ if (configDir) scan(path9.join(configDir, "skills"), "user");
8708
8821
  const home = os3.homedir();
8709
- const homeDir = home ? path8.resolve(home) : null;
8710
- let current = path8.resolve(opts.cwd);
8822
+ const homeDir = home ? path9.resolve(home) : null;
8823
+ let current = path9.resolve(opts.cwd);
8711
8824
  while (true) {
8712
- if (current !== homeDir) scan(path8.join(current, ".claude", "skills"), "project");
8713
- const parent = path8.dirname(current);
8825
+ if (current !== homeDir) scan(path9.join(current, ".claude", "skills"), "project");
8826
+ const parent = path9.dirname(current);
8714
8827
  if (parent === current) break;
8715
8828
  current = parent;
8716
8829
  }
@@ -8744,10 +8857,10 @@ function dropNativelyLoadedSkills(skills, native) {
8744
8857
  }
8745
8858
  function readBundledSkillInfos() {
8746
8859
  return discoverBundledSkills().flatMap((skill) => {
8747
- const file = path8.join(skill.dir, "SKILL.md");
8860
+ const file = path9.join(skill.dir, "SKILL.md");
8748
8861
  let text;
8749
8862
  try {
8750
- text = fs6.readFileSync(file, "utf8");
8863
+ text = fs7.readFileSync(file, "utf8");
8751
8864
  } catch {
8752
8865
  return [];
8753
8866
  }
@@ -8771,15 +8884,15 @@ function readBundledSkillInfos() {
8771
8884
  }
8772
8885
  function linkSkill(source, target) {
8773
8886
  try {
8774
- fs6.symlinkSync(source, target, process.platform === "win32" ? "junction" : "dir");
8887
+ fs7.symlinkSync(source, target, process.platform === "win32" ? "junction" : "dir");
8775
8888
  return;
8776
8889
  } catch {
8777
- fs6.cpSync(source, target, { recursive: true, dereference: true });
8890
+ fs7.cpSync(source, target, { recursive: true, dereference: true });
8778
8891
  }
8779
8892
  }
8780
8893
  function fileExists2(p) {
8781
8894
  try {
8782
- return fs6.statSync(p).isFile();
8895
+ return fs7.statSync(p).isFile();
8783
8896
  } catch {
8784
8897
  return false;
8785
8898
  }
@@ -8788,14 +8901,14 @@ function buildSkillPluginDir(skills) {
8788
8901
  if (skills.length === 0) return null;
8789
8902
  const fingerprint = skills.map((s) => `${s.name}\0${s.dir}`).join("\n");
8790
8903
  const hash = crypto3.createHash("sha256").update(fingerprint).digest("hex").slice(0, 12);
8791
- const root = path8.join(pluginTmpDir(), `skills-${hash}`);
8792
- const manifest = path8.join(root, ".claude-plugin", "plugin.json");
8904
+ const root = path9.join(pluginTmpDir(), `skills-${hash}`);
8905
+ const manifest = path9.join(root, ".claude-plugin", "plugin.json");
8793
8906
  if (fileExists2(manifest)) return root;
8794
8907
  try {
8795
- fs6.rmSync(root, { recursive: true, force: true });
8796
- fs6.mkdirSync(path8.join(root, ".claude-plugin"), { recursive: true });
8797
- fs6.mkdirSync(path8.join(root, "skills"), { recursive: true });
8798
- fs6.writeFileSync(
8908
+ fs7.rmSync(root, { recursive: true, force: true });
8909
+ fs7.mkdirSync(path9.join(root, ".claude-plugin"), { recursive: true });
8910
+ fs7.mkdirSync(path9.join(root, "skills"), { recursive: true });
8911
+ fs7.writeFileSync(
8799
8912
  manifest,
8800
8913
  JSON.stringify(
8801
8914
  {
@@ -8808,7 +8921,7 @@ function buildSkillPluginDir(skills) {
8808
8921
  { encoding: "utf8", mode: 384 }
8809
8922
  );
8810
8923
  for (const skill of skills) {
8811
- linkSkill(skill.dir, path8.join(root, "skills", skill.name));
8924
+ linkSkill(skill.dir, path9.join(root, "skills", skill.name));
8812
8925
  }
8813
8926
  } catch (err) {
8814
8927
  log.warn("failed to stage opencode skill plugin dir", {
@@ -8824,7 +8937,7 @@ function registerBundledSkillPath(config) {
8824
8937
  if (!dir) return false;
8825
8938
  config.skills ??= {};
8826
8939
  const paths = config.skills.paths ??= [];
8827
- if (paths.some((p) => path8.resolve(p) === dir)) return false;
8940
+ if (paths.some((p) => path9.resolve(p) === dir)) return false;
8828
8941
  paths.push(dir);
8829
8942
  return true;
8830
8943
  }
@@ -8878,12 +8991,13 @@ import { join as join10 } from "path";
8878
8991
 
8879
8992
  // src/claude-session-bun.ts
8880
8993
  import * as os4 from "os";
8881
- import * as fs7 from "fs";
8882
- import * as path9 from "path";
8994
+ import * as fs8 from "fs";
8995
+ import * as path10 from "path";
8883
8996
  import { execFileSync } from "child_process";
8884
8997
  import { randomUUID as randomUUID4 } from "crypto";
8998
+ import { StringDecoder } from "string_decoder";
8885
8999
  function resolveClaude(cmd = "claude") {
8886
- if (path9.isAbsolute(cmd) && fs7.existsSync(cmd)) return cmd;
9000
+ if (path10.isAbsolute(cmd) && fs8.existsSync(cmd)) return cmd;
8887
9001
  const viaBun = Bun.which(cmd);
8888
9002
  if (viaBun) return viaBun;
8889
9003
  const isWin = os4.platform() === "win32";
@@ -8892,24 +9006,30 @@ function resolveClaude(cmd = "claude") {
8892
9006
  encoding: "utf8",
8893
9007
  stdio: ["ignore", "pipe", "ignore"]
8894
9008
  });
8895
- const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) => fs7.existsSync(p));
9009
+ const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) => fs8.existsSync(p));
8896
9010
  if (first) return first;
8897
9011
  } catch {
8898
9012
  }
8899
9013
  throw new Error(`Could not resolve command on PATH: ${cmd}`);
8900
9014
  }
8901
9015
  function encodeCwd(cwd) {
8902
- const resolved = path9.resolve(cwd);
9016
+ const resolved = path10.resolve(cwd);
8903
9017
  let real = resolved;
8904
9018
  try {
8905
- real = fs7.realpathSync.native(resolved);
9019
+ real = fs8.realpathSync.native(resolved);
8906
9020
  } catch {
8907
9021
  }
8908
9022
  return real.replace(/[^a-zA-Z0-9]/g, "-");
8909
9023
  }
8910
9024
  var bunPtySpawner = (argv, opts) => {
8911
9025
  const [command, ...args] = argv;
8912
- return Bun.spawn([resolveClaude(command ?? "claude"), ...args], {
9026
+ const resolved = resolveClaude(command ?? "claude");
9027
+ if (os4.platform() === "win32" && isBatchFile(resolved)) {
9028
+ throw new Error(
9029
+ `The interactive transport cannot run a Windows batch shim (${resolved}). Point \`cliPath\` at claude.exe, or use the default headless transport.`
9030
+ );
9031
+ }
9032
+ return Bun.spawn([resolved, ...args], {
8913
9033
  cwd: opts.cwd,
8914
9034
  env: opts.env,
8915
9035
  terminal: {
@@ -9011,6 +9131,14 @@ function classifyScreen(text) {
9011
9131
  }
9012
9132
  return null;
9013
9133
  }
9134
+ function interactiveTranscriptPath(opts) {
9135
+ return path10.join(
9136
+ resolveConfigDir(opts.configDir),
9137
+ "projects",
9138
+ encodeCwd(path10.resolve(opts.cwd)),
9139
+ `${opts.sessionId}.jsonl`
9140
+ );
9141
+ }
9014
9142
  function configDirEnv(configDir) {
9015
9143
  return configDir ? { CLAUDE_CONFIG_DIR: configDir } : {};
9016
9144
  }
@@ -9124,6 +9252,123 @@ function isInterruptRecord(rec) {
9124
9252
  function isTurnDurationRecord(rec) {
9125
9253
  return rec?.type === "system" && rec.subtype === "turn_duration";
9126
9254
  }
9255
+ var TRANSCRIPT_CHUNK_BYTES = 1 << 20;
9256
+ var TranscriptCursor = class {
9257
+ /** The path is asked for on every poll rather than kept, because it is a
9258
+ * field of the session and a test may repoint it; a path that changes is
9259
+ * a different file, which the `dev:ino` check already starts over on. */
9260
+ constructor(resolvePath) {
9261
+ this.resolvePath = resolvePath;
9262
+ }
9263
+ resolvePath;
9264
+ /** Bytes of the current file already read. */
9265
+ offset = 0;
9266
+ /** `dev:ino` of the file those bytes came from, null before the first
9267
+ * successful stat. A different one is a new file, not growth. */
9268
+ fileId = null;
9269
+ /** Holds back an incomplete trailing UTF-8 sequence between two reads. */
9270
+ decoder = new StringDecoder("utf8");
9271
+ /** What was read after the last newline: a record the CLI is part way
9272
+ * through writing, completed by a later read. */
9273
+ partial = "";
9274
+ /** Complete lines seen since byte 0 of the current file. */
9275
+ seen = 0;
9276
+ /** Complete lines handed on: the line-index cursor. */
9277
+ taken = 0;
9278
+ /** Complete lines read and not handed on yet, oldest first. */
9279
+ queue = [];
9280
+ stats = { polls: 0, reads: 0, bytes: 0 };
9281
+ /** Complete lines in the file now. */
9282
+ lineCount() {
9283
+ this.poll(true);
9284
+ return this.seen;
9285
+ }
9286
+ /** The complete lines not handed on yet, oldest first. They stay queued
9287
+ * until `take` says how many of them were used. */
9288
+ pending() {
9289
+ this.poll(true);
9290
+ return this.queue;
9291
+ }
9292
+ /** Whether the file holds a complete line this cursor has not handed on. */
9293
+ hasUnread() {
9294
+ return this.pending().length > 0;
9295
+ }
9296
+ /** Mark the first `count` pending lines as handed on. */
9297
+ take(count) {
9298
+ const used = Math.min(count, this.queue.length);
9299
+ if (used <= 0) return;
9300
+ this.queue.splice(0, used);
9301
+ this.taken += used;
9302
+ }
9303
+ /** Hand on nothing of what is in the file now: the records written before a
9304
+ * turn started belong to the turn before it. */
9305
+ skipToEnd() {
9306
+ this.poll(false);
9307
+ this.taken = Math.max(this.taken, this.seen);
9308
+ this.queue.length = 0;
9309
+ }
9310
+ /** Read whatever was appended since the last look. */
9311
+ poll(collect) {
9312
+ this.stats.polls++;
9313
+ const filePath = this.resolvePath();
9314
+ let stat;
9315
+ try {
9316
+ stat = fs8.statSync(filePath);
9317
+ } catch {
9318
+ return;
9319
+ }
9320
+ const fileId = `${stat.dev}:${stat.ino}`;
9321
+ if (this.fileId !== null && (fileId !== this.fileId || stat.size < this.offset)) {
9322
+ this.offset = 0;
9323
+ this.seen = 0;
9324
+ this.partial = "";
9325
+ this.decoder = new StringDecoder("utf8");
9326
+ this.queue.length = 0;
9327
+ }
9328
+ this.fileId = fileId;
9329
+ if (stat.size <= this.offset) return;
9330
+ let fd = null;
9331
+ try {
9332
+ fd = fs8.openSync(filePath, "r");
9333
+ const buffer = Buffer.allocUnsafe(
9334
+ Math.min(TRANSCRIPT_CHUNK_BYTES, stat.size - this.offset)
9335
+ );
9336
+ while (this.offset < stat.size) {
9337
+ const want = Math.min(buffer.length, stat.size - this.offset);
9338
+ const read = fs8.readSync(fd, buffer, 0, want, this.offset);
9339
+ this.stats.reads++;
9340
+ if (read <= 0) break;
9341
+ this.offset += read;
9342
+ this.stats.bytes += read;
9343
+ this.ingest(this.decoder.write(buffer.subarray(0, read)), collect);
9344
+ }
9345
+ } catch {
9346
+ } finally {
9347
+ if (fd !== null) {
9348
+ try {
9349
+ fs8.closeSync(fd);
9350
+ } catch {
9351
+ }
9352
+ }
9353
+ }
9354
+ }
9355
+ /** Split decoded text into complete lines, keeping the unterminated tail. */
9356
+ ingest(text, collect) {
9357
+ if (!text) return;
9358
+ this.partial += text;
9359
+ let start = 0;
9360
+ for (; ; ) {
9361
+ const newline = this.partial.indexOf("\n", start);
9362
+ if (newline === -1) break;
9363
+ if (collect && this.seen >= this.taken) {
9364
+ this.queue.push(this.partial.slice(start, newline));
9365
+ }
9366
+ this.seen++;
9367
+ start = newline + 1;
9368
+ }
9369
+ if (start > 0) this.partial = this.partial.slice(start);
9370
+ }
9371
+ };
9127
9372
  var normalizedPrompt = (text) => text.replace(/\s+/g, " ").trim();
9128
9373
  function isOwnPromptRecord(rec, normalized) {
9129
9374
  const content = rec?.type === "user" ? rec.message?.content : void 0;
@@ -9131,12 +9376,12 @@ function isOwnPromptRecord(rec, normalized) {
9131
9376
  }
9132
9377
  function resolveConfigDir(configDir) {
9133
9378
  const value = configDir ?? process.env.CLAUDE_CONFIG_DIR;
9134
- if (!value) return path9.join(os4.homedir(), ".claude");
9379
+ if (!value) return path10.join(os4.homedir(), ".claude");
9135
9380
  if (value === "~") return os4.homedir();
9136
9381
  if (value.startsWith("~/") || value.startsWith("~\\")) {
9137
- return path9.join(os4.homedir(), value.slice(2));
9382
+ return path10.join(os4.homedir(), value.slice(2));
9138
9383
  }
9139
- return path9.resolve(value);
9384
+ return path10.resolve(value);
9140
9385
  }
9141
9386
  function fatalScreenMessage(kind, configDir, cwd) {
9142
9387
  if (kind === "trust") {
@@ -9155,8 +9400,8 @@ var ClaudeSession = class {
9155
9400
  /** The newest terminal output, ANSI included, for diagnostics. */
9156
9401
  raw = "";
9157
9402
  proc = null;
9158
- cursor = 0;
9159
- // index into transcript split('\n')
9403
+ /** The transcript, read forwards only; its `taken` is the old line cursor. */
9404
+ transcript;
9160
9405
  lastDataAt = 0;
9161
9406
  exited = false;
9162
9407
  exitCode = null;
@@ -9200,7 +9445,7 @@ var ClaudeSession = class {
9200
9445
  abandonTurn = false;
9201
9446
  o;
9202
9447
  constructor(opts = {}) {
9203
- this.cwd = path9.resolve(opts.cwd ?? process.cwd());
9448
+ this.cwd = path10.resolve(opts.cwd ?? process.cwd());
9204
9449
  this.configDir = resolveConfigDir(opts.configDir);
9205
9450
  this.explicitConfigDir = opts.configDir ? this.configDir : void 0;
9206
9451
  this.signal = opts.signal;
@@ -9212,12 +9457,13 @@ var ClaudeSession = class {
9212
9457
  this.onExit = opts.onExit;
9213
9458
  this.spawnPty = opts.spawnPty ?? bunPtySpawner;
9214
9459
  this.sessionId = opts.resumeSessionId ?? randomUUID4();
9215
- this.jsonlPath = path9.join(
9460
+ this.jsonlPath = path10.join(
9216
9461
  this.configDir,
9217
9462
  "projects",
9218
9463
  encodeCwd(this.cwd),
9219
9464
  `${this.sessionId}.jsonl`
9220
9465
  );
9466
+ this.transcript = new TranscriptCursor(() => this.jsonlPath);
9221
9467
  this.o = {
9222
9468
  cwd: this.cwd,
9223
9469
  cliPath: opts.cliPath,
@@ -9322,7 +9568,12 @@ var ClaudeSession = class {
9322
9568
  };
9323
9569
  proc.exited.then((code) => markExited(typeof code === "number" ? code : null)).catch(() => markExited(null));
9324
9570
  await this.waitForBoot();
9325
- this.cursor = this.lineCount();
9571
+ this.transcript.skipToEnd();
9572
+ }
9573
+ /** How much this session has read off the transcript. Read-only, and the
9574
+ * one thing `scripts/bench-transcript-poll.ts` measures. */
9575
+ get transcriptStats() {
9576
+ return this.transcript.stats;
9326
9577
  }
9327
9578
  onData(chunk) {
9328
9579
  this.lastDataAt = Date.now();
@@ -9469,22 +9720,10 @@ var ClaudeSession = class {
9469
9720
  while (Date.now() < until) {
9470
9721
  await delay(80);
9471
9722
  if (this.aborted || this.exited) return;
9472
- if (this.lineCount() > this.cursor) return;
9723
+ if (this.transcript.hasUnread()) return;
9473
9724
  }
9474
9725
  }
9475
9726
  }
9476
- readRawLines() {
9477
- try {
9478
- return fs7.readFileSync(this.jsonlPath, "utf8").split("\n");
9479
- } catch {
9480
- return [];
9481
- }
9482
- }
9483
- /** Count of complete lines (split('\n') minus the trailing/partial element). */
9484
- lineCount() {
9485
- const lines = this.readRawLines();
9486
- return lines.length > 0 ? lines.length - 1 : 0;
9487
- }
9488
9727
  rawTail(max = 600) {
9489
9728
  const clean = stripTerminal(this.raw).replace(/\s+/g, " ").trim();
9490
9729
  return clean.length > max ? clean.slice(-max) : clean;
@@ -9517,7 +9756,7 @@ var ClaudeSession = class {
9517
9756
  this.turn = new Promise((resolve6) => settle = resolve6);
9518
9757
  this.turnDenied = [];
9519
9758
  this.abandonTurn = false;
9520
- this.cursor = Math.max(this.cursor, this.lineCount());
9759
+ this.transcript.skipToEnd();
9521
9760
  this.screen = "";
9522
9761
  try {
9523
9762
  if (this.o.bracketedPaste) {
@@ -9582,30 +9821,26 @@ var ClaudeSession = class {
9582
9821
  /**
9583
9822
  * Read every complete transcript record past the cursor into the running
9584
9823
  * turn. Returns whether anything new was read. The one place a record is
9585
- * interpreted, shared by the poll loop and `flushTranscript`.
9824
+ * interpreted, shared by the poll loop and `flushTranscript`, and the only
9825
+ * one that consumes what `TranscriptCursor` has queued.
9586
9826
  */
9587
9827
  scanTranscript() {
9588
9828
  const scan = this.scan;
9589
9829
  if (!scan || scan.end) return false;
9590
- const lines = this.readRawLines();
9591
- const lastComplete = lines.length - 1;
9592
- if (lastComplete <= this.cursor) return false;
9593
- for (let i = this.cursor; i < lastComplete && !scan.end; i++) {
9830
+ const lines = this.transcript.pending().slice();
9831
+ if (lines.length === 0) return false;
9832
+ let used = 0;
9833
+ for (let i = 0; i < lines.length && !scan.end; i++) {
9594
9834
  const s = lines[i];
9595
- if (!s || !s.trim()) {
9596
- this.cursor = i + 1;
9597
- continue;
9598
- }
9835
+ used = i + 1;
9836
+ if (!s || !s.trim()) continue;
9599
9837
  let rec = null;
9600
9838
  try {
9601
9839
  rec = JSON.parse(s);
9602
9840
  } catch {
9603
9841
  }
9604
9842
  if (scan.awaitingOwnPrompt !== null) {
9605
- if (!isOwnPromptRecord(rec, scan.awaitingOwnPrompt)) {
9606
- this.cursor = i + 1;
9607
- continue;
9608
- }
9843
+ if (!isOwnPromptRecord(rec, scan.awaitingOwnPrompt)) continue;
9609
9844
  scan.awaitingOwnPrompt = null;
9610
9845
  }
9611
9846
  if (rec) this.notePlanApproval(rec);
@@ -9625,9 +9860,8 @@ var ClaudeSession = class {
9625
9860
  scan.end = scan.stopReason ? "stop" : "ended";
9626
9861
  }
9627
9862
  }
9628
- this.cursor = i + 1;
9629
9863
  }
9630
- if (!scan.end) this.cursor = lastComplete;
9864
+ this.transcript.take(used);
9631
9865
  scan.lastBeat = Date.now();
9632
9866
  scan.stopSeenAt = Date.now();
9633
9867
  return true;
@@ -10415,14 +10649,74 @@ function spawnInteractiveProcess(opts) {
10415
10649
  return ap;
10416
10650
  }
10417
10651
 
10652
+ // src/account-transcript.ts
10653
+ import { randomUUID as randomUUID6 } from "crypto";
10654
+ import { constants as fsConstants } from "fs";
10655
+ import { chmod as chmod2, copyFile, lstat as lstat2, mkdir as mkdir2 } from "fs/promises";
10656
+ import path11 from "path";
10657
+ var transcriptPath = interactiveTranscriptPath;
10658
+ function configDirForAccount(account) {
10659
+ const normalized = normalizeAccountName(account || DEFAULT_ACCOUNT);
10660
+ return resolveConfigDir(
10661
+ normalized === DEFAULT_ACCOUNT ? void 0 : accountConfigDirPath(normalized)
10662
+ );
10663
+ }
10664
+ async function carryTranscriptToAccount(input) {
10665
+ const { sessionId, cwd, targetConfigDir } = input;
10666
+ const targetOwn = transcriptPath({ configDir: targetConfigDir, cwd, sessionId });
10667
+ const targetOwnStat = await statTranscript(targetOwn);
10668
+ if (targetOwnStat.kind === "other") {
10669
+ return { kind: "refused", reason: "target-not-a-file", detail: targetOwn };
10670
+ }
10671
+ let live;
10672
+ for (const dir of dedupe2([targetConfigDir, ...input.accountConfigDirs])) {
10673
+ const candidate = transcriptPath({ configDir: dir, cwd, sessionId });
10674
+ const stat = candidate === targetOwn ? targetOwnStat : await statTranscript(candidate);
10675
+ if (stat.kind !== "file") continue;
10676
+ if (!live || stat.size > live.size) live = { file: candidate, size: stat.size };
10677
+ }
10678
+ if (!live) return { kind: "refused", reason: "no-transcript-anywhere" };
10679
+ if (live.file === targetOwn) return { kind: "already-there", sessionId };
10680
+ const renamed = targetOwnStat.kind === "file";
10681
+ const carriedId = renamed ? (input.newSessionId ?? randomUUID6)() : sessionId;
10682
+ const target = renamed ? transcriptPath({ configDir: targetConfigDir, cwd, sessionId: carriedId }) : targetOwn;
10683
+ try {
10684
+ await mkdir2(path11.dirname(target), { recursive: true, mode: 448 });
10685
+ await copyFile(live.file, target, fsConstants.COPYFILE_EXCL);
10686
+ await chmod2(target, 384);
10687
+ } catch (err) {
10688
+ return { kind: "refused", reason: "copy-failed", detail: String(err) };
10689
+ }
10690
+ return { kind: "carried", sessionId: carriedId, from: live.file, to: target, renamed };
10691
+ }
10692
+ async function statTranscript(file) {
10693
+ try {
10694
+ const stat = await lstat2(file);
10695
+ return stat.isFile() ? { kind: "file", size: stat.size } : { kind: "other", size: 0 };
10696
+ } catch {
10697
+ return { kind: "none", size: 0 };
10698
+ }
10699
+ }
10700
+ function dedupe2(dirs) {
10701
+ const seen = /* @__PURE__ */ new Set();
10702
+ const out = [];
10703
+ for (const dir of dirs) {
10704
+ const resolved = path11.resolve(dir);
10705
+ if (seen.has(resolved)) continue;
10706
+ seen.add(resolved);
10707
+ out.push(resolved);
10708
+ }
10709
+ return out;
10710
+ }
10711
+
10418
10712
  // src/prompts.ts
10419
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
10713
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
10420
10714
  import { homedir as homedir8 } from "os";
10421
- import { randomUUID as randomUUID6 } from "crypto";
10715
+ import { randomUUID as randomUUID7 } from "crypto";
10422
10716
  import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join11 } from "path";
10423
- function readPromptFileIfPresent(path11) {
10717
+ function readPromptFileIfPresent(path13) {
10424
10718
  try {
10425
- const content = readFileSync7(path11, "utf8").trim();
10719
+ const content = readFileSync6(path13, "utf8").trim();
10426
10720
  return content || void 0;
10427
10721
  } catch {
10428
10722
  return void 0;
@@ -10527,9 +10821,9 @@ function extractSystemMessages(prompt) {
10527
10821
  }
10528
10822
  var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLAUDE.local.md"]);
10529
10823
  function userAuthoredInstructions(forwarded, opts = {}) {
10530
- const readFile2 = opts.readFile ?? ((path11) => {
10824
+ const readFile2 = opts.readFile ?? ((path13) => {
10531
10825
  try {
10532
- return readFileSync7(path11, "utf8");
10826
+ return readFileSync6(path13, "utf8");
10533
10827
  } catch {
10534
10828
  return null;
10535
10829
  }
@@ -10541,16 +10835,16 @@ function userAuthoredInstructions(forwarded, opts = {}) {
10541
10835
  }
10542
10836
  const seen = /* @__PURE__ */ new Set();
10543
10837
  for (const match of forwarded.matchAll(/^Instructions from: (.+)$/gm)) {
10544
- const path11 = match[1].trim();
10545
- if (seen.has(path11) || !isAbsolute3(path11)) continue;
10546
- if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path11))) continue;
10547
- seen.add(path11);
10548
- const file = readFile2(path11);
10838
+ const path13 = match[1].trim();
10839
+ if (seen.has(path13) || !isAbsolute3(path13)) continue;
10840
+ if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path13))) continue;
10841
+ seen.add(path13);
10842
+ const file = readFile2(path13);
10549
10843
  if (!file?.trim()) continue;
10550
10844
  const start = match.index + match[0].length + 1;
10551
10845
  const content = forwarded.startsWith(file, start) ? file : forwarded.startsWith(file.trimEnd(), start) ? file.trimEnd() : null;
10552
10846
  if (content === null) continue;
10553
- out.push({ text: `Instructions from: ${path11}
10847
+ out.push({ text: `Instructions from: ${path13}
10554
10848
  ${content}`, content });
10555
10849
  }
10556
10850
  return out;
@@ -10587,10 +10881,10 @@ ${options.compressionSummary.trim()}`
10587
10881
  if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
10588
10882
  const content = parts.join("\n\n");
10589
10883
  if (!content) return void 0;
10590
- const path11 = join11(pluginTmpDir(), `opencode-cc-sys-${randomUUID6()}.md`);
10884
+ const path13 = join11(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
10591
10885
  try {
10592
- writeFileSync6(path11, content, { encoding: "utf8", mode: 384 });
10593
- return path11;
10886
+ writeFileSync6(path13, content, { encoding: "utf8", mode: 384 });
10887
+ return path13;
10594
10888
  } catch (err) {
10595
10889
  log.warn("failed to write system prompt file", { error: String(err) });
10596
10890
  return void 0;
@@ -13606,12 +13900,51 @@ var ClaudeCodeLanguageModel = class {
13606
13900
  failedOver: failover.failedOver
13607
13901
  });
13608
13902
  deleteActiveProcess(sk);
13903
+ }
13904
+ let conversationOnTarget = false;
13905
+ const multiAccount = (this.config.failoverAccounts?.length ?? 0) > 1;
13906
+ const sessionBeforeAccountCheck = getClaudeSessionId(sk);
13907
+ if (!compactionMode && multiAccount && this.config.crossAccountResume !== false && sessionBeforeAccountCheck) {
13908
+ const carry = await carryTranscriptToAccount({
13909
+ sessionId: sessionBeforeAccountCheck,
13910
+ cwd,
13911
+ targetConfigDir: resolveConfigDir(this.skillBridgeSpawn(failover).configDir),
13912
+ // Every account's own directory, plus this model's configured one:
13913
+ // `configDir` is an option, so an account's directory is not always
13914
+ // the `~/.claude-<name>` the name alone would build.
13915
+ accountConfigDirs: [
13916
+ ...(this.config.failoverAccounts ?? []).map(configDirForAccount),
13917
+ resolveConfigDir(this.config.configDir)
13918
+ ]
13919
+ });
13920
+ if (carry.kind === "carried" || carry.kind === "already-there") {
13921
+ if (carry.sessionId !== sessionBeforeAccountCheck) {
13922
+ setClaudeSessionId(sk, carry.sessionId);
13923
+ }
13924
+ conversationOnTarget = true;
13925
+ if (carry.kind === "carried") {
13926
+ log.notice("carried this conversation's claude transcript to the other account", {
13927
+ sessionKey: sk,
13928
+ renamed: carry.renamed,
13929
+ sessionId: carry.sessionId,
13930
+ failedOver: failover.failedOver
13931
+ });
13932
+ }
13933
+ } else if (!getActiveProcess(sk)) {
13934
+ log.notice("replaying this conversation as text on the other account", {
13935
+ sessionKey: sk,
13936
+ reason: carry.reason,
13937
+ failedOver: failover.failedOver
13938
+ });
13939
+ deleteClaudeSessionId(sk);
13940
+ }
13941
+ } else if (!compactionMode && sessionBeforeAccountCheck && processForAccount?.cliPath && processForAccount.cliPath !== cliPath) {
13609
13942
  deleteClaudeSessionId(sk);
13610
13943
  }
13611
13944
  const hasExistingSession = !!getClaudeSessionId(sk);
13612
13945
  const hasActiveProcess = !!getActiveProcess(sk);
13613
13946
  let includeHistoryContext = !hasExistingSession && !hasActiveProcess && hasPriorConversation;
13614
- if (failoverAnswer?.kind === "switch" && hasPriorConversation) {
13947
+ if (failoverAnswer?.kind === "switch" && hasPriorConversation && !conversationOnTarget) {
13615
13948
  includeHistoryContext = true;
13616
13949
  }
13617
13950
  let forkFromClaudeSessionId;
@@ -13657,12 +13990,12 @@ var ClaudeCodeLanguageModel = class {
13657
13990
  let replayReason = "no-session-for-key";
13658
13991
  if (this.config.resumeAfterRestart !== false && includeHistoryContext && !compactionMode && !forkFromClaudeSessionId && failoverAnswer?.kind !== "switch") {
13659
13992
  const configDir = resolveConfigDir(this.config.configDir);
13660
- const transcriptPath = (id) => join12(configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
13993
+ const transcriptPath2 = (id) => join12(configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
13661
13994
  const resumePoint = findResumePoint({
13662
13995
  sessionKey: sk,
13663
13996
  prompt: options.prompt,
13664
13997
  cliPath,
13665
- transcriptPath,
13998
+ transcriptPath: transcriptPath2,
13666
13999
  onRefused: (reason) => {
13667
14000
  replayReason = reason;
13668
14001
  }
@@ -13679,7 +14012,7 @@ var ClaudeCodeLanguageModel = class {
13679
14012
  sessionKey: sk,
13680
14013
  prompt: options.prompt,
13681
14014
  cliPath,
13682
- transcriptPath,
14015
+ transcriptPath: transcriptPath2,
13683
14016
  // `claudeSessionIsWriting`, not `getActiveProcess`: this walks every
13684
14017
  // sibling key, and that one refreshes LRU order and cancels idle
13685
14018
  // timers on everything it touches (the same reason the fork lookup
@@ -13724,7 +14057,11 @@ var ClaudeCodeLanguageModel = class {
13724
14057
  log.info("sending plan approval decision to claude", { sk });
13725
14058
  }
13726
14059
  const previousPendingProxyCalls = compactionMode ? [] : getPendingProxyCalls(sk);
13727
- const effectivePrompt = failoverAnswer?.kind === "switch" ? buildFailoverContinuationPrompt(options.prompt, failoverAnswer.target) : options.prompt;
14060
+ const effectivePrompt = failoverAnswer?.kind === "switch" ? buildFailoverContinuationPrompt(
14061
+ options.prompt,
14062
+ failoverAnswer.target,
14063
+ conversationOnTarget
14064
+ ) : options.prompt;
13728
14065
  const userMsg = exitPlanModeQuestionResult ?? getClaudeUserMessage(effectivePrompt, includeHistoryContext, {
13729
14066
  compactionMode,
13730
14067
  compactionInstructions: useInteractive && compactionMode ? extractSystemMessages(effectivePrompt).join("\n\n") : void 0,
@@ -14406,7 +14743,8 @@ var ClaudeCodeLanguageModel = class {
14406
14743
  candidates: failoverAccounts,
14407
14744
  resetsAt: state.accountLimitHit?.resetsAt,
14408
14745
  window: state.accountLimitHit?.window,
14409
- reason: state.accountLimitHit || !state.accountBlock ? void 0 : describeAccountBlock(state.accountBlock)
14746
+ reason: state.accountLimitHit || !state.accountBlock ? void 0 : describeAccountBlock(state.accountBlock),
14747
+ carryTranscript: self.config.crossAccountResume !== false
14410
14748
  });
14411
14749
  log.warn(
14412
14750
  `Claude account "${sourceAccount}" ${state.accountLimitHit || !state.accountBlock ? "is out of usage" : `cannot serve (${state.accountBlock})`}; asking which account to continue on.`,
@@ -14908,7 +15246,7 @@ var ClaudeCodeLanguageModel = class {
14908
15246
  import {
14909
15247
  existsSync as existsSync5,
14910
15248
  mkdirSync as mkdirSync5,
14911
- readFileSync as readFileSync8,
15249
+ readFileSync as readFileSync7,
14912
15250
  realpathSync as realpathSync4,
14913
15251
  rmSync as rmSync3,
14914
15252
  writeFileSync as writeFileSync7
@@ -14935,7 +15273,7 @@ function userIntendsToUseUnscoped() {
14935
15273
  const cfg = userOpencodeJsonPath();
14936
15274
  if (!existsSync5(cfg)) return false;
14937
15275
  try {
14938
- const json = JSON.parse(readFileSync8(cfg, "utf8"));
15276
+ const json = JSON.parse(readFileSync7(cfg, "utf8"));
14939
15277
  const plugins = json.plugin;
14940
15278
  if (!Array.isArray(plugins)) return false;
14941
15279
  return plugins.some(
@@ -14956,7 +15294,7 @@ function ourLoadedDir() {
14956
15294
  function ourVersion(dir) {
14957
15295
  if (!dir) return null;
14958
15296
  try {
14959
- const pkg = JSON.parse(readFileSync8(join13(dir, "package.json"), "utf8"));
15297
+ const pkg = JSON.parse(readFileSync7(join13(dir, "package.json"), "utf8"));
14960
15298
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
14961
15299
  } catch {
14962
15300
  return null;
@@ -14968,7 +15306,7 @@ function markerPath() {
14968
15306
  }
14969
15307
  function markerVersion() {
14970
15308
  try {
14971
- const marker = JSON.parse(readFileSync8(markerPath(), "utf8"));
15309
+ const marker = JSON.parse(readFileSync7(markerPath(), "utf8"));
14972
15310
  return typeof marker.version === "string" ? marker.version : null;
14973
15311
  } catch {
14974
15312
  return null;
@@ -15025,7 +15363,7 @@ function cleanupOne(cacheRoot, ourDir) {
15025
15363
  if (!existsSync5(pkgJsonPath)) return;
15026
15364
  let pkg = {};
15027
15365
  try {
15028
- pkg = JSON.parse(readFileSync8(pkgJsonPath, "utf8"));
15366
+ pkg = JSON.parse(readFileSync7(pkgJsonPath, "utf8"));
15029
15367
  } catch {
15030
15368
  return;
15031
15369
  }
@@ -15044,7 +15382,7 @@ function cleanupOne(cacheRoot, ourDir) {
15044
15382
  const cachePkgJson = join13(cacheRoot, "package.json");
15045
15383
  if (!existsSync5(cachePkgJson)) return;
15046
15384
  try {
15047
- const cfg = JSON.parse(readFileSync8(cachePkgJson, "utf8"));
15385
+ const cfg = JSON.parse(readFileSync7(cachePkgJson, "utf8"));
15048
15386
  if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
15049
15387
  delete cfg.dependencies[STALE_PACKAGE_NAME];
15050
15388
  writeFileSync7(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
@@ -15124,7 +15462,7 @@ function registerOpencodeLogSink(client) {
15124
15462
  }
15125
15463
 
15126
15464
  // src/v2-client.ts
15127
- import path10 from "path";
15465
+ import path12 from "path";
15128
15466
  function formatAgentTypeList(agents) {
15129
15467
  const lines = agents.flatMap((agent) => {
15130
15468
  if (agent.hidden === true || agent.mode === "primary") return [];
@@ -15153,7 +15491,7 @@ function toV1Session(info) {
15153
15491
  if (!info || typeof info !== "object") return void 0;
15154
15492
  const session = info;
15155
15493
  const root = session.location?.directory;
15156
- const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path10.resolve(root, session.subpath) : root : void 0;
15494
+ const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path12.resolve(root, session.subpath) : root : void 0;
15157
15495
  return { ...info, directory, parentID: session.parentID };
15158
15496
  }
15159
15497
  function toV1Messages(messages) {
@@ -15712,6 +16050,7 @@ function createClaudeCode(settings = {}) {
15712
16050
  forkSessions: settings.forkSessions === true,
15713
16051
  resumeAfterRestart: settings.resumeAfterRestart !== false,
15714
16052
  resumeAcrossModelChanges: settings.resumeAcrossModelChanges !== false,
16053
+ crossAccountResume: settings.crossAccountResume !== false,
15715
16054
  transport: settings.transport,
15716
16055
  interactive: settings.interactive,
15717
16056
  interactiveBypass: settings.interactiveBypass,