@khalilgharbaoui/opencode-claude-code-plugin 0.63.1 → 0.63.3

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 join20 } from "path";
2
+ import { join as join21 } from "path";
3
3
 
4
4
  // src/ids.ts
5
5
  import { randomInt } from "crypto";
@@ -152,7 +152,7 @@ function translateStreamForHost(stream, dialect = "v1") {
152
152
  }
153
153
 
154
154
  // src/account-failover.ts
155
- import { homedir as homedir3 } from "os";
155
+ import { homedir as homedir4 } from "os";
156
156
 
157
157
  // src/accounts.ts
158
158
  import {
@@ -1008,6 +1008,11 @@ function snapshotAccountUsage(now = Date.now()) {
1008
1008
  return refreshed(now);
1009
1009
  }
1010
1010
 
1011
+ // src/cli-events.ts
1012
+ import { readFileSync as readFileSync2, statSync as statSync3 } from "fs";
1013
+ import { homedir as homedir3 } from "os";
1014
+ import { join as join3, resolve } from "path";
1015
+
1011
1016
  // src/types.ts
1012
1017
  var READ_ONLY_PERMISSION_MODE = "read-only";
1013
1018
  var DEFAULT_PROXY_TOOL_NAMES = [
@@ -1274,6 +1279,35 @@ var warnedMcpSkips = /* @__PURE__ */ new Set();
1274
1279
  var lastMcpServerErrors = /* @__PURE__ */ new Map();
1275
1280
  var warnedPluginDiagnostics = /* @__PURE__ */ new Set();
1276
1281
  var lastPluginLoadFailures = /* @__PURE__ */ new Map();
1282
+ var lastUnconnectedConnectors = /* @__PURE__ */ new Map();
1283
+ var CLAUDE_AI_CONNECTOR_PREFIX = "claude.ai ";
1284
+ function cliGlobalConfigPath(configDir) {
1285
+ const dir = configDir ?? process.env.CLAUDE_CONFIG_DIR;
1286
+ if (!dir) return join3(homedir3(), ".claude.json");
1287
+ const expanded = dir === "~" ? homedir3() : dir.startsWith("~/") ? join3(homedir3(), dir.slice(2)) : dir;
1288
+ return join3(resolve(expanded), ".claude.json");
1289
+ }
1290
+ var everConnectedCache = /* @__PURE__ */ new Map();
1291
+ function claudeAiConnectorsEverConnected(configDir) {
1292
+ const file = cliGlobalConfigPath(configDir);
1293
+ try {
1294
+ const { mtimeMs } = statSync3(file);
1295
+ const cached2 = everConnectedCache.get(file);
1296
+ if (cached2 && cached2.mtimeMs === mtimeMs) return cached2.names;
1297
+ const list2 = JSON.parse(readFileSync2(file, "utf8")).claudeAiMcpEverConnected;
1298
+ const names = Array.isArray(list2) ? new Set(list2.filter((name) => typeof name === "string")) : void 0;
1299
+ everConnectedCache.set(file, { mtimeMs, names });
1300
+ return names;
1301
+ } catch {
1302
+ return void 0;
1303
+ }
1304
+ }
1305
+ function isUnconnectedConnector(server2, everConnected) {
1306
+ return everConnected !== void 0 && server2.status === "needs-auth" && server2.name.startsWith(CLAUDE_AI_CONNECTOR_PREFIX) && !everConnected.has(server2.name);
1307
+ }
1308
+ function snapshotUnconnectedConnectors() {
1309
+ return [...lastUnconnectedConnectors].map(([name, status]) => ({ name, status }));
1310
+ }
1277
1311
  function snapshotMcpServerErrors() {
1278
1312
  return [...lastMcpServerErrors.values()];
1279
1313
  }
@@ -1324,16 +1358,30 @@ function reportSystemInit(msg, options = {}) {
1324
1358
  warnedMcpSkips.add(key2);
1325
1359
  log.warn(message, { server: error.name, type: error.type });
1326
1360
  }
1327
- for (const server2 of info.mcpServers) {
1328
- if (server2.status === "connected") continue;
1361
+ const unusable = info.mcpServers.filter((server2) => server2.status !== "connected");
1362
+ const everConnected = unusable.some((server2) => server2.name.startsWith(CLAUDE_AI_CONNECTOR_PREFIX)) ? claudeAiConnectorsEverConnected(options.configDir) : void 0;
1363
+ const newlyUnusable = /* @__PURE__ */ new Map();
1364
+ const newlyUnconnected = [];
1365
+ for (const server2 of unusable) {
1366
+ const unconnected = isUnconnectedConnector(server2, everConnected);
1367
+ if (unconnected) lastUnconnectedConnectors.set(server2.name, server2.status);
1329
1368
  const key2 = `${server2.name}:${server2.status}`;
1330
- const message = `MCP server "${server2.name}" is ${server2.status} in Claude Code; its tools are not available to the model this session.`;
1331
1369
  if (warnedMcpFailures.has(key2)) {
1332
- log.debug(message, { server: server2.name, status: server2.status });
1370
+ log.debug("MCP server still unavailable in Claude Code", { server: server2.name, status: server2.status });
1333
1371
  continue;
1334
1372
  }
1335
1373
  warnedMcpFailures.add(key2);
1336
- log.warn(message, { server: server2.name, status: server2.status });
1374
+ if (unconnected) newlyUnconnected.push(server2.name);
1375
+ else newlyUnusable.set(server2.status, [...newlyUnusable.get(server2.status) ?? [], server2.name]);
1376
+ }
1377
+ if (newlyUnconnected.length > 0) {
1378
+ log.info("claude.ai connectors this account never connected need authentication; not warning about them", {
1379
+ servers: newlyUnconnected
1380
+ });
1381
+ }
1382
+ for (const [status, names] of newlyUnusable) {
1383
+ const message = names.length === 1 ? `MCP server "${names[0]}" is ${status} in Claude Code; its tools are not available to the model this session.` : `${names.length} MCP servers are ${status} in Claude Code; their tools are not available to the model this session: ${names.join(", ")}.`;
1384
+ log.warn(message, names.length === 1 ? { server: names[0], status } : { servers: names, status });
1337
1385
  }
1338
1386
  const apiKeyMessage = apiKeySourceWarning(info.apiKeySource, options.ignoreAnthropicApiKey);
1339
1387
  if (!apiKeyMessage) return;
@@ -1846,7 +1894,7 @@ function accountBlockKind(msg) {
1846
1894
  function describeAccountBlock(kind) {
1847
1895
  return ACCOUNT_BLOCKS[kind].what;
1848
1896
  }
1849
- function loginCommandFor(configDir, home = homedir3()) {
1897
+ function loginCommandFor(configDir, home = homedir4()) {
1850
1898
  if (!configDir) return "claude auth login";
1851
1899
  const shown = configDir === home || configDir.startsWith(`${home}/`) ? `~${configDir.slice(home.length)}` : configDir;
1852
1900
  return `CLAUDE_CONFIG_DIR=${shown} claude auth login`;
@@ -2228,7 +2276,7 @@ function failoverUntil(resetsAt) {
2228
2276
  }
2229
2277
 
2230
2278
  // src/subagent-dispatch.ts
2231
- import { join as join5 } from "path";
2279
+ import { join as join6 } from "path";
2232
2280
 
2233
2281
  // src/agent-models.ts
2234
2282
  import { readFile, readdir } from "fs/promises";
@@ -2882,9 +2930,9 @@ function agentDirectories(home, projectDirectory) {
2882
2930
  }
2883
2931
 
2884
2932
  // src/dispatch-choice-store.ts
2885
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, renameSync as renameSync3, writeFileSync as writeFileSync2 } from "fs";
2886
- import { homedir as homedir4 } from "os";
2887
- import { join as join3 } from "path";
2933
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync3, writeFileSync as writeFileSync2 } from "fs";
2934
+ import { homedir as homedir5 } from "os";
2935
+ import { join as join4 } from "path";
2888
2936
  var MAX_DISPATCH_CHOICE_SESSIONS = 128;
2889
2937
  var MAX_DISPATCH_CHOICES_PER_SESSION = 32;
2890
2938
  var DISPATCH_CHOICE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
@@ -2892,8 +2940,8 @@ var sessions = null;
2892
2940
  var pathOverride2 = null;
2893
2941
  function dispatchChoiceStorePath() {
2894
2942
  if (pathOverride2) return pathOverride2;
2895
- const stateRoot = process.env.XDG_STATE_HOME || join3(homedir4(), ".local", "state");
2896
- return join3(stateRoot, "opencode-claude-code-plugin", "subagent-dispatch.json");
2943
+ const stateRoot = process.env.XDG_STATE_HOME || join4(homedir5(), ".local", "state");
2944
+ return join4(stateRoot, "opencode-claude-code-plugin", "subagent-dispatch.json");
2897
2945
  }
2898
2946
  function isChoice(value) {
2899
2947
  const choice = value;
@@ -2919,7 +2967,7 @@ function trimChoices(choices) {
2919
2967
  function readFile2(file) {
2920
2968
  const out = /* @__PURE__ */ new Map();
2921
2969
  try {
2922
- const parsed = JSON.parse(readFileSync2(file, "utf8"));
2970
+ const parsed = JSON.parse(readFileSync3(file, "utf8"));
2923
2971
  const entries = parsed && typeof parsed === "object" ? parsed.sessions : null;
2924
2972
  if (!entries || typeof entries !== "object") return out;
2925
2973
  const cutoff = Date.now() - DISPATCH_CHOICE_MAX_AGE_MS;
@@ -2941,7 +2989,7 @@ function save(update) {
2941
2989
  const kept = [...merged.entries()].filter(([, record]) => record.updatedAt >= cutoff).filter(([, record]) => Object.keys(record.choices).length > 0).sort((a, b) => a[1].updatedAt - b[1].updatedAt).slice(-MAX_DISPATCH_CHOICE_SESSIONS);
2942
2990
  sessions = new Map(kept);
2943
2991
  try {
2944
- mkdirSync3(join3(file, ".."), { recursive: true, mode: 448 });
2992
+ mkdirSync3(join4(file, ".."), { recursive: true, mode: 448 });
2945
2993
  const tmp = `${file}.${process.pid}.tmp`;
2946
2994
  writeFileSync2(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {
2947
2995
  mode: 384
@@ -5291,12 +5339,12 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
5291
5339
  });
5292
5340
  let timer = null;
5293
5341
  const result = await new Promise(
5294
- (resolve7, reject2) => {
5342
+ (resolve8, reject2) => {
5295
5343
  const entry = {
5296
5344
  id: callId,
5297
5345
  toolName,
5298
5346
  input,
5299
- resolve: resolve7,
5347
+ resolve: resolve8,
5300
5348
  reject: reject2,
5301
5349
  channel
5302
5350
  };
@@ -5385,11 +5433,11 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
5385
5433
  }
5386
5434
  }
5387
5435
  });
5388
- await new Promise((resolve7, reject2) => {
5436
+ await new Promise((resolve8, reject2) => {
5389
5437
  server2.once("error", reject2);
5390
5438
  server2.listen(0, "127.0.0.1", () => {
5391
5439
  server2.off("error", reject2);
5392
- resolve7();
5440
+ resolve8();
5393
5441
  });
5394
5442
  });
5395
5443
  const addr = server2.address();
@@ -5446,8 +5494,8 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
5446
5494
  entry.reject(new Error(SERVER_CLOSED_MESSAGE));
5447
5495
  }
5448
5496
  pending2.clear();
5449
- await new Promise((resolve7) => {
5450
- server2.close(() => resolve7());
5497
+ await new Promise((resolve8) => {
5498
+ server2.close(() => resolve8());
5451
5499
  });
5452
5500
  if (configFilePath) {
5453
5501
  try {
@@ -5620,10 +5668,10 @@ function resolveMcpProxyToolDefs(options) {
5620
5668
  return { defs, coveredServers, matchedServers };
5621
5669
  }
5622
5670
  function readBody(req) {
5623
- return new Promise((resolve7, reject) => {
5671
+ return new Promise((resolve8, reject) => {
5624
5672
  const chunks = [];
5625
5673
  req.on("data", (chunk) => chunks.push(chunk));
5626
- req.on("end", () => resolve7(Buffer.concat(chunks).toString("utf8")));
5674
+ req.on("end", () => resolve8(Buffer.concat(chunks).toString("utf8")));
5627
5675
  req.on("error", reject);
5628
5676
  });
5629
5677
  }
@@ -6819,7 +6867,7 @@ async function settleSessionRunState(sessionID, options = {}) {
6819
6867
  if (state === "busy") return "busy";
6820
6868
  if (state !== "unknown") last = state;
6821
6869
  if (Date.now() - started2 >= windowMs) return last;
6822
- await new Promise((resolve7) => setTimeout(resolve7, pollMs));
6870
+ await new Promise((resolve8) => setTimeout(resolve8, pollMs));
6823
6871
  }
6824
6872
  }
6825
6873
  var DEFAULT_MCP_CONNECT_WAIT_MS = 3e3;
@@ -6872,7 +6920,7 @@ async function waitForPendingMcpServers(first, budgetMs, signal) {
6872
6920
  for (; ; ) {
6873
6921
  if (Date.now() - started2 >= budgetMs) break;
6874
6922
  if (signal?.aborted) break;
6875
- await new Promise((resolve7) => setTimeout(resolve7, MCP_CONNECT_POLL_MS));
6923
+ await new Promise((resolve8) => setTimeout(resolve8, MCP_CONNECT_POLL_MS));
6876
6924
  if (signal?.aborted) break;
6877
6925
  const next = await fetchRuntimeMcpStatus();
6878
6926
  if (!next) break;
@@ -7101,8 +7149,8 @@ async function countOtherRunningWithin(exclude, budgetMs) {
7101
7149
  worker
7102
7150
  );
7103
7151
  let timer;
7104
- const deadline = new Promise((resolve7) => {
7105
- timer = setTimeout(() => resolve7("over"), budgetMs);
7152
+ const deadline = new Promise((resolve8) => {
7153
+ timer = setTimeout(() => resolve8("over"), budgetMs);
7106
7154
  timer.unref?.();
7107
7155
  });
7108
7156
  try {
@@ -7312,17 +7360,17 @@ async function cancelBackgroundTask(input, options) {
7312
7360
  }
7313
7361
 
7314
7362
  // src/session-resume-store.ts
7315
- import { existsSync as existsSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync4, writeFileSync as writeFileSync5 } from "fs";
7316
- import { homedir as homedir7 } from "os";
7317
- import { join as join9 } from "path";
7363
+ import { existsSync as existsSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync6, renameSync as renameSync4, writeFileSync as writeFileSync5 } from "fs";
7364
+ import { homedir as homedir8 } from "os";
7365
+ import { join as join10 } from "path";
7318
7366
  var MAX_RESUME_RECORDS = 256;
7319
7367
  var RESUME_RECORD_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
7320
7368
  var records = null;
7321
7369
  var pathOverride3 = null;
7322
7370
  function resumeStorePath() {
7323
7371
  if (pathOverride3) return pathOverride3;
7324
- const stateRoot = process.env.XDG_STATE_HOME || join9(homedir7(), ".local", "state");
7325
- return join9(stateRoot, "opencode-claude-code-plugin", "claude-sessions.json");
7372
+ const stateRoot = process.env.XDG_STATE_HOME || join10(homedir8(), ".local", "state");
7373
+ return join10(stateRoot, "opencode-claude-code-plugin", "claude-sessions.json");
7326
7374
  }
7327
7375
  function isDigest(value) {
7328
7376
  const entry = value;
@@ -7336,7 +7384,7 @@ function load2() {
7336
7384
  if (records) return records;
7337
7385
  records = /* @__PURE__ */ new Map();
7338
7386
  try {
7339
- const parsed = JSON.parse(readFileSync5(resumeStorePath(), "utf8"));
7387
+ const parsed = JSON.parse(readFileSync6(resumeStorePath(), "utf8"));
7340
7388
  const entries = parsed && typeof parsed === "object" ? parsed.sessions : null;
7341
7389
  if (entries && typeof entries === "object") {
7342
7390
  const cutoff = Date.now() - RESUME_RECORD_MAX_AGE_MS;
@@ -7351,7 +7399,7 @@ function save2(update) {
7351
7399
  const file = resumeStorePath();
7352
7400
  const merged = /* @__PURE__ */ new Map();
7353
7401
  try {
7354
- const onDisk = JSON.parse(readFileSync5(file, "utf8"))?.sessions;
7402
+ const onDisk = JSON.parse(readFileSync6(file, "utf8"))?.sessions;
7355
7403
  if (onDisk && typeof onDisk === "object") {
7356
7404
  for (const [key, record] of Object.entries(onDisk)) {
7357
7405
  if (isRecord2(record)) merged.set(key, record);
@@ -7364,7 +7412,7 @@ function save2(update) {
7364
7412
  const kept = [...merged.entries()].filter(([, record]) => record.updatedAt >= cutoff).sort((a, b) => a[1].updatedAt - b[1].updatedAt).slice(-MAX_RESUME_RECORDS);
7365
7413
  records = new Map(kept);
7366
7414
  try {
7367
- mkdirSync5(join9(file, ".."), { recursive: true, mode: 448 });
7415
+ mkdirSync5(join10(file, ".."), { recursive: true, mode: 448 });
7368
7416
  const tmp = `${file}.${process.pid}.tmp`;
7369
7417
  writeFileSync5(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {
7370
7418
  mode: 384
@@ -7790,7 +7838,7 @@ async function requestSideQuestion(activeProcess, question, options) {
7790
7838
  }
7791
7839
  });
7792
7840
  pendingProcesses.add(proc);
7793
- return new Promise((resolve7, reject) => {
7841
+ return new Promise((resolve8, reject) => {
7794
7842
  const event = `side-question:${requestId}`;
7795
7843
  let settled = false;
7796
7844
  let sent = false;
@@ -7845,7 +7893,7 @@ async function requestSideQuestion(activeProcess, question, options) {
7845
7893
  }
7846
7894
  settled = true;
7847
7895
  cleanup();
7848
- resolve7({ response: result.response, synthetic: result.synthetic });
7896
+ resolve8({ response: result.response, synthetic: result.synthetic });
7849
7897
  };
7850
7898
  const timer = setTimeout(() => {
7851
7899
  fail(new Error(`/btw timed out after ${timeoutMs}ms.`), true);
@@ -7891,7 +7939,7 @@ function safely(action) {
7891
7939
  var ownMember;
7892
7940
  var ownFollowers = 0;
7893
7941
  var ownServed = false;
7894
- function join10() {
7942
+ function join11() {
7895
7943
  if (!ownMember) return;
7896
7944
  const state = relayState();
7897
7945
  if (state.members.has(ownMember)) return;
@@ -7904,11 +7952,11 @@ function leave() {
7904
7952
  }
7905
7953
  function defineOwnRelayMember(member) {
7906
7954
  ownMember = member;
7907
- if (ownServed) join10();
7955
+ if (ownServed) join11();
7908
7956
  }
7909
7957
  function noteRelayMemberServing() {
7910
7958
  ownServed = true;
7911
- join10();
7959
+ join11();
7912
7960
  }
7913
7961
  function relayMemberHasServed() {
7914
7962
  return ownServed;
@@ -7918,7 +7966,7 @@ function leaveSessionEventRelay() {
7918
7966
  }
7919
7967
  function openRelayFollower() {
7920
7968
  const state = relayState();
7921
- join10();
7969
+ join11();
7922
7970
  ownFollowers += 1;
7923
7971
  state.followers += 1;
7924
7972
  if (state.followers === 1) {
@@ -8108,16 +8156,16 @@ function isTurnInFlight(ap) {
8108
8156
  }
8109
8157
  function awaitTurnIdle(ap, timeoutMs) {
8110
8158
  if (!ap.turnInFlight) return Promise.resolve(true);
8111
- return new Promise((resolve7) => {
8159
+ return new Promise((resolve8) => {
8112
8160
  const wake = () => {
8113
8161
  clearTimeout(timer);
8114
- resolve7(true);
8162
+ resolve8(true);
8115
8163
  };
8116
8164
  const timer = setTimeout(() => {
8117
8165
  const waiters = ap.turnIdleWaiters ?? [];
8118
8166
  const at = waiters.indexOf(wake);
8119
8167
  if (at >= 0) waiters.splice(at, 1);
8120
- resolve7(false);
8168
+ resolve8(false);
8121
8169
  }, timeoutMs);
8122
8170
  (ap.turnIdleWaiters ??= []).push(wake);
8123
8171
  });
@@ -8281,14 +8329,14 @@ function hasProcessExited(proc) {
8281
8329
  }
8282
8330
  function waitForProcessExit(proc, timeoutMs) {
8283
8331
  if (hasProcessExited(proc)) return Promise.resolve(true);
8284
- return new Promise((resolve7) => {
8332
+ return new Promise((resolve8) => {
8285
8333
  const onExit = () => {
8286
8334
  clearTimeout(timer);
8287
- resolve7(true);
8335
+ resolve8(true);
8288
8336
  };
8289
8337
  const timer = setTimeout(() => {
8290
8338
  proc.off("exit", onExit);
8291
- resolve7(hasProcessExited(proc));
8339
+ resolve8(hasProcessExited(proc));
8292
8340
  }, timeoutMs);
8293
8341
  proc.once("exit", onExit);
8294
8342
  });
@@ -8442,7 +8490,26 @@ function spawnIdentity(key) {
8442
8490
  }
8443
8491
  return parts.join("::");
8444
8492
  }
8445
- function invalidateOtherSpawnVariants(baseKey, effort) {
8493
+ function spawnHasPendingWork(key) {
8494
+ const active = activeProcesses.get(key);
8495
+ return !!(getPendingProxyCalls(key).length || hasExitPlanModeQuestions(key) || active?.pendingProxyCompletions?.size || active && (active.lineEmitter.listenerCount("line") > 0 || isSideQuestionPending(active)));
8496
+ }
8497
+ function conversationIdentity(key) {
8498
+ const parts = spawnIdentity(key).split("::");
8499
+ if (parts.length !== 5 || parts[2] === "compaction" || parts[3] === "default") return void 0;
8500
+ parts[1] = parts[1].replace(/^[^@]*/, "*");
8501
+ return parts.join("::");
8502
+ }
8503
+ function findBusySpawnVariant(requestedKey) {
8504
+ const identity = conversationIdentity(requestedKey);
8505
+ if (!identity || spawnHasPendingWork(requestedKey)) return void 0;
8506
+ for (const key of /* @__PURE__ */ new Set([...activeProcesses.keys(), ...claudeSessions.keys()])) {
8507
+ if (key === requestedKey || conversationIdentity(key) !== identity) continue;
8508
+ if (spawnHasPendingWork(key)) return key;
8509
+ }
8510
+ return void 0;
8511
+ }
8512
+ function invalidateOtherSpawnVariants(currentKey, options = {}) {
8446
8513
  const levels = [
8447
8514
  void 0,
8448
8515
  "minimal",
@@ -8452,7 +8519,7 @@ function invalidateOtherSpawnVariants(baseKey, effort) {
8452
8519
  "xhigh",
8453
8520
  "max"
8454
8521
  ];
8455
- const currentKey = effortSessionKey(baseKey, effort);
8522
+ const baseKey = currentKey.replace(/::effort=[a-z]+$/, "");
8456
8523
  const identity = spawnIdentity(currentKey);
8457
8524
  const staleKeys = [
8458
8525
  .../* @__PURE__ */ new Set([
@@ -8462,16 +8529,16 @@ function invalidateOtherSpawnVariants(baseKey, effort) {
8462
8529
  ])
8463
8530
  ].filter((key) => key !== currentKey && spawnIdentity(key) === identity);
8464
8531
  for (const key of staleKeys) {
8465
- const active = activeProcesses.get(key);
8466
- if (getPendingProxyCalls(key).length || hasExitPlanModeQuestions(key) || active?.pendingProxyCompletions?.size || active && (active.lineEmitter.listenerCount("line") > 0 || isSideQuestionPending(active))) {
8532
+ if (spawnHasPendingWork(key)) {
8467
8533
  throw new Error(
8468
8534
  "Cannot change reasoning effort, prompt cache TTL, maxTurns or maxBudgetUsd while this conversation's previous claude process has pending work. Finish that work under its original settings first."
8469
8535
  );
8470
8536
  }
8471
8537
  }
8538
+ const carry = options.carryConversation !== false;
8472
8539
  for (const key of staleKeys) {
8473
8540
  deleteActiveProcess(key);
8474
- if (transferClaudeSession(key, currentKey)) {
8541
+ if (carry && transferClaudeSession(key, currentKey)) {
8475
8542
  clearCompression(key);
8476
8543
  clearBackgroundTasks(key);
8477
8544
  continue;
@@ -8893,7 +8960,7 @@ async function waitForSessionIdle(client, sessionID, options = {}) {
8893
8960
  if (options.stop?.()) return true;
8894
8961
  if (await sessionStatus(client, sessionID) !== "busy") return true;
8895
8962
  if (Date.now() - started2 >= timeoutMs) return false;
8896
- await new Promise((resolve7) => setTimeout(resolve7, pollMs));
8963
+ await new Promise((resolve8) => setTimeout(resolve8, pollMs));
8897
8964
  }
8898
8965
  }
8899
8966
  async function deliverAsideInline(client, sessionID, text, options = {}) {
@@ -8904,7 +8971,7 @@ async function deliverAsideInline(client, sessionID, text, options = {}) {
8904
8971
  if (emitAsideInline(sessionID, text)) return true;
8905
8972
  if (Date.now() - started2 >= timeoutMs) return false;
8906
8973
  if (await sessionStatus(client, sessionID) !== "busy") return false;
8907
- await new Promise((resolve7) => setTimeout(resolve7, pollMs));
8974
+ await new Promise((resolve8) => setTimeout(resolve8, pollMs));
8908
8975
  }
8909
8976
  }
8910
8977
  async function waitForAsideProcess(client, sessionID, options = {}) {
@@ -8925,7 +8992,7 @@ async function waitForAsideProcess(client, sessionID, options = {}) {
8925
8992
  log.warn("btw: a turn is running but no claude process appeared for it", { sessionID, waitedMs });
8926
8993
  return void 0;
8927
8994
  }
8928
- await new Promise((resolve7) => setTimeout(resolve7, pollMs));
8995
+ await new Promise((resolve8) => setTimeout(resolve8, pollMs));
8929
8996
  }
8930
8997
  }
8931
8998
  async function settleSessionBusy(client, sessionID, active, options = {}) {
@@ -8937,7 +9004,7 @@ async function settleSessionBusy(client, sessionID, active, options = {}) {
8937
9004
  if (status === "busy") return true;
8938
9005
  if (status === "unknown") return isProcessBusy(active);
8939
9006
  if (Date.now() - started2 >= settleMs) return false;
8940
- await new Promise((resolve7) => setTimeout(resolve7, pollMs));
9007
+ await new Promise((resolve8) => setTimeout(resolve8, pollMs));
8941
9008
  }
8942
9009
  }
8943
9010
  function errorText(error) {
@@ -8981,10 +9048,10 @@ async function runBtwCommand(client, input, options = {}) {
8981
9048
  let inlineDone = false;
8982
9049
  let markInlineDelivered = () => {
8983
9050
  };
8984
- const inlineDelivered = new Promise((resolve7) => {
9051
+ const inlineDelivered = new Promise((resolve8) => {
8985
9052
  markInlineDelivered = () => {
8986
9053
  inlineDone = true;
8987
- resolve7("inline");
9054
+ resolve8("inline");
8988
9055
  };
8989
9056
  });
8990
9057
  if (isSideQuestionPending(active)) {
@@ -9234,7 +9301,7 @@ function snapshotBudgets() {
9234
9301
  // src/diagnostic-bundle.ts
9235
9302
  import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
9236
9303
  import { openSync, readSync, closeSync, fstatSync, realpathSync } from "fs";
9237
- import { homedir as homedir8 } from "os";
9304
+ import { homedir as homedir9 } from "os";
9238
9305
 
9239
9306
  // src/log-messages.ts
9240
9307
  var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
@@ -9333,6 +9400,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
9333
9400
  "falling back to the next model in the chain",
9334
9401
  "fast mode is in cooldown after a rate limit; this turn runs at standard speed and is billed at standard Opus rates, not the fast price shown in the model picker.",
9335
9402
  "file part without data, skipping",
9403
+ "finishing the turn in progress under its original settings",
9336
9404
  "forkSessions is on but this claude CLI has no --fork-session; replaying the conversation instead",
9337
9405
  "forking the parent conversation's claude session instead of replaying it",
9338
9406
  "holding a subagent dispatch for the operator's answer",
@@ -9683,7 +9751,7 @@ function createIdHasher(salt = randomBytes2(16).toString("hex")) {
9683
9751
  return (raw) => createHash4("sha256").update(salt).update("\0").update(raw).digest("hex").slice(0, 8);
9684
9752
  }
9685
9753
  function createRedactionContext(options = {}) {
9686
- const home = options.home ?? homedir8();
9754
+ const home = options.home ?? homedir9();
9687
9755
  const homes = /* @__PURE__ */ new Set();
9688
9756
  if (home) homes.add(home);
9689
9757
  if (home) {
@@ -10216,7 +10284,7 @@ function parsePlanUsage(stdout) {
10216
10284
  }
10217
10285
  function runCli(cliPath, args, timeoutMs, env) {
10218
10286
  const plan = planClaudeSpawn(cliPath, args);
10219
- return new Promise((resolve7, reject) => {
10287
+ return new Promise((resolve8, reject) => {
10220
10288
  execFile2(
10221
10289
  plan.file,
10222
10290
  plan.args,
@@ -10230,9 +10298,9 @@ function runCli(cliPath, args, timeoutMs, env) {
10230
10298
  windowsVerbatimArguments: plan.windowsVerbatimArguments
10231
10299
  },
10232
10300
  (error, stdout) => {
10233
- if (stdout && stdout.trim()) return resolve7(stdout);
10301
+ if (stdout && stdout.trim()) return resolve8(stdout);
10234
10302
  if (error) return reject(error);
10235
- resolve7(stdout ?? "");
10303
+ resolve8(stdout ?? "");
10236
10304
  }
10237
10305
  );
10238
10306
  });
@@ -10270,22 +10338,22 @@ async function fetchPlanUsage(cliPath, options = {}) {
10270
10338
  }
10271
10339
 
10272
10340
  // src/turn-history.ts
10273
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync6, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
10274
- import { homedir as homedir11 } from "os";
10275
- import { join as join12 } from "path";
10341
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, renameSync as renameSync6, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
10342
+ import { homedir as homedir12 } from "os";
10343
+ import { join as join13 } from "path";
10276
10344
 
10277
10345
  // src/turn-progress-store.ts
10278
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync6, renameSync as renameSync5, statSync as statSync5, writeFileSync as writeFileSync6 } from "fs";
10279
- import { homedir as homedir10 } from "os";
10280
- import { join as join11 } from "path";
10346
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync7, renameSync as renameSync5, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
10347
+ import { homedir as homedir11 } from "os";
10348
+ import { join as join12 } from "path";
10281
10349
  var MAX_TURN_PROGRESS = 64;
10282
10350
  var TURN_PROGRESS_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
10283
10351
  var TAG = /^[0-9a-f]{8}$/;
10284
10352
  var pathOverride4 = null;
10285
10353
  function turnProgressStorePath() {
10286
10354
  if (pathOverride4) return pathOverride4;
10287
- const stateRoot = process.env.XDG_STATE_HOME || join11(homedir10(), ".local", "state");
10288
- return join11(stateRoot, "opencode-claude-code-plugin", "turn-progress.json");
10355
+ const stateRoot = process.env.XDG_STATE_HOME || join12(homedir11(), ".local", "state");
10356
+ return join12(stateRoot, "opencode-claude-code-plugin", "turn-progress.json");
10289
10357
  }
10290
10358
  function isProgress(value) {
10291
10359
  const record = value;
@@ -10296,7 +10364,7 @@ function isProgress(value) {
10296
10364
  function readEntries(file, now) {
10297
10365
  const out = /* @__PURE__ */ new Map();
10298
10366
  try {
10299
- const parsed = JSON.parse(readFileSync6(file, "utf8"));
10367
+ const parsed = JSON.parse(readFileSync7(file, "utf8"));
10300
10368
  const entries = parsed && typeof parsed === "object" ? parsed.turns : null;
10301
10369
  if (!entries || typeof entries !== "object") return out;
10302
10370
  const cutoff = now - TURN_PROGRESS_MAX_AGE_MS;
@@ -10315,7 +10383,7 @@ function recordTurnProgress(tag, progress, now = Date.now()) {
10315
10383
  merged.set(tag, { ...record, out: Math.round(record.out) });
10316
10384
  const kept = [...merged.entries()].slice(-MAX_TURN_PROGRESS);
10317
10385
  try {
10318
- mkdirSync6(join11(file, ".."), { recursive: true, mode: 448 });
10386
+ mkdirSync6(join12(file, ".."), { recursive: true, mode: 448 });
10319
10387
  const tmp = `${file}.${process.pid}.tmp`;
10320
10388
  writeFileSync6(tmp, JSON.stringify({ version: 1, turns: Object.fromEntries(kept) }), { mode: 384 });
10321
10389
  renameSync5(tmp, file);
@@ -10333,8 +10401,8 @@ var SESSION_TAG = /^[0-9a-f]{8}$/;
10333
10401
  var pathOverride5 = null;
10334
10402
  function turnHistoryStorePath() {
10335
10403
  if (pathOverride5) return pathOverride5;
10336
- const stateRoot = process.env.XDG_STATE_HOME || join12(homedir11(), ".local", "state");
10337
- return join12(stateRoot, "opencode-claude-code-plugin", "turn-history.json");
10404
+ const stateRoot = process.env.XDG_STATE_HOME || join13(homedir12(), ".local", "state");
10405
+ return join13(stateRoot, "opencode-claude-code-plugin", "turn-history.json");
10338
10406
  }
10339
10407
  function turnHistoryKey(agent, model) {
10340
10408
  return `${agent || "main"}/${model}`;
@@ -10493,7 +10561,7 @@ function cleanSample(sample) {
10493
10561
  function readEntries2(file, now) {
10494
10562
  const out = /* @__PURE__ */ new Map();
10495
10563
  try {
10496
- const parsed = JSON.parse(readFileSync7(file, "utf8"));
10564
+ const parsed = JSON.parse(readFileSync8(file, "utf8"));
10497
10565
  const entries = parsed && typeof parsed === "object" ? parsed.turns : null;
10498
10566
  if (!entries || typeof entries !== "object") return out;
10499
10567
  const cutoff = now - TURN_SAMPLE_MAX_AGE_MS;
@@ -10518,7 +10586,7 @@ function recordTurnSample(key, sample) {
10518
10586
  merged.set(key, samples);
10519
10587
  const kept = bounded2(merged);
10520
10588
  try {
10521
- mkdirSync7(join12(file, ".."), { recursive: true, mode: 448 });
10589
+ mkdirSync7(join13(file, ".."), { recursive: true, mode: 448 });
10522
10590
  const tmp = `${file}.${process.pid}.tmp`;
10523
10591
  writeFileSync7(tmp, JSON.stringify({ version: 1, turns: Object.fromEntries(kept) }), { mode: 384 });
10524
10592
  renameSync6(tmp, file);
@@ -10535,7 +10603,7 @@ function createTurnHistoryReader(file = turnHistoryStorePath) {
10535
10603
  const path15 = file();
10536
10604
  let next = "";
10537
10605
  try {
10538
- const stats = statSync6(path15);
10606
+ const stats = statSync7(path15);
10539
10607
  next = `${stats.mtimeMs}:${stats.size}:${stats.ino}`;
10540
10608
  } catch {
10541
10609
  next = "missing";
@@ -11575,6 +11643,21 @@ function formatDoctorReport(report) {
11575
11643
  lines.push("");
11576
11644
  lines.push("A skipped server is missing from the model's tools with no other sign of it.");
11577
11645
  }
11646
+ const unconnected = report.unconnectedConnectors ?? [];
11647
+ if (unconnected.length > 0) {
11648
+ lines.push("");
11649
+ lines.push("**claude.ai connectors this account never connected**");
11650
+ lines.push("");
11651
+ lines.push("| connector | status |");
11652
+ lines.push("|---|---|");
11653
+ for (const connector of unconnected) {
11654
+ lines.push(`| ${tableCell(connector.name)} | \`${connector.status}\` |`);
11655
+ }
11656
+ lines.push("");
11657
+ lines.push(
11658
+ "They come with the claude.ai organisation, not from your config, and load no tools. Connect one at claude.ai to give the model its tools; until then the plugin does not warn about it."
11659
+ );
11660
+ }
11578
11661
  if (report.pluginLoadFailures.length > 0) {
11579
11662
  lines.push("");
11580
11663
  lines.push("**Plugins Claude Code did not load**");
@@ -11951,6 +12034,7 @@ async function gatherDoctorReport(options) {
11951
12034
  proxyServers,
11952
12035
  readRules: snapshotReadRuleMapping(),
11953
12036
  mcpServerErrors: snapshotMcpServerErrors(),
12037
+ unconnectedConnectors: snapshotUnconnectedConnectors(),
11954
12038
  pluginLoadFailures: snapshotPluginLoadFailures(),
11955
12039
  hookFailures: snapshotHookFailures(),
11956
12040
  planUsage,
@@ -12043,6 +12127,7 @@ async function buildDoctorReport(options) {
12043
12127
  pendingCalls: report.pendingCalls.length,
12044
12128
  proxyServers: report.proxyServers.map((server2) => server2.auth.status),
12045
12129
  mcpServerErrors: report.mcpServerErrors.length,
12130
+ unconnectedConnectors: report.unconnectedConnectors?.length ?? 0,
12046
12131
  pluginLoadFailures: report.pluginLoadFailures.length,
12047
12132
  hookFailures: report.hookFailures.length,
12048
12133
  planUsage: report.planUsage.status,
@@ -13101,7 +13186,7 @@ import { EventEmitter as EventEmitter4 } from "events";
13101
13186
  import { randomUUID as randomUUID5 } from "crypto";
13102
13187
  import { existsSync as existsSync4, writeFileSync as writeFileSync9 } from "fs";
13103
13188
  import { unlink as unlink2 } from "fs/promises";
13104
- import { join as join17 } from "path";
13189
+ import { join as join18 } from "path";
13105
13190
 
13106
13191
  // src/claude-session-bun.ts
13107
13192
  import * as os6 from "os";
@@ -13868,7 +13953,7 @@ var ClaudeSession = class {
13868
13953
  throw new Error("session not started or already exited");
13869
13954
  if (this.turn) throw new Error("a turn is already running in this session");
13870
13955
  let settle;
13871
- this.turn = new Promise((resolve7) => settle = resolve7);
13956
+ this.turn = new Promise((resolve8) => settle = resolve8);
13872
13957
  this.turnDenied = [];
13873
13958
  this.abandonTurn = false;
13874
13959
  this.transcript.skipToEnd();
@@ -14283,7 +14368,7 @@ ${text2}`
14283
14368
  }
14284
14369
  function stageImage(data, extension) {
14285
14370
  try {
14286
- const file = join17(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
14371
+ const file = join18(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
14287
14372
  writeFileSync9(file, data, { mode: 384, flag: "wx" });
14288
14373
  return file;
14289
14374
  } catch (err) {
@@ -14765,17 +14850,17 @@ function spawnInteractiveProcess(opts) {
14765
14850
  }
14766
14851
 
14767
14852
  // src/spawn-record-store.ts
14768
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync11, renameSync as renameSync7, statSync as statSync10, writeFileSync as writeFileSync10 } from "fs";
14769
- import { homedir as homedir14 } from "os";
14770
- import { join as join18 } from "path";
14853
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync12, renameSync as renameSync7, statSync as statSync11, writeFileSync as writeFileSync10 } from "fs";
14854
+ import { homedir as homedir15 } from "os";
14855
+ import { join as join19 } from "path";
14771
14856
  var MAX_SPAWN_RECORDS = 256;
14772
14857
  var SPAWN_RECORD_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
14773
14858
  var pathOverride6 = null;
14774
14859
  var lastWritten = /* @__PURE__ */ new Map();
14775
14860
  function spawnRecordStorePath() {
14776
14861
  if (pathOverride6) return pathOverride6;
14777
- const stateRoot = process.env.XDG_STATE_HOME || join18(homedir14(), ".local", "state");
14778
- return join18(stateRoot, "opencode-claude-code-plugin", "session-spawns.json");
14862
+ const stateRoot = process.env.XDG_STATE_HOME || join19(homedir15(), ".local", "state");
14863
+ return join19(stateRoot, "opencode-claude-code-plugin", "session-spawns.json");
14779
14864
  }
14780
14865
  function isRecord5(value) {
14781
14866
  const record = value;
@@ -14788,7 +14873,7 @@ function isRecord5(value) {
14788
14873
  function readEntries3(file) {
14789
14874
  const out = /* @__PURE__ */ new Map();
14790
14875
  try {
14791
- const parsed = JSON.parse(readFileSync11(file, "utf8"));
14876
+ const parsed = JSON.parse(readFileSync12(file, "utf8"));
14792
14877
  const entries = parsed && typeof parsed === "object" ? parsed.sessions : null;
14793
14878
  if (!entries || typeof entries !== "object") return out;
14794
14879
  const cutoff = Date.now() - SPAWN_RECORD_MAX_AGE_MS;
@@ -14827,7 +14912,7 @@ ${record.agent ?? ""}`;
14827
14912
  const cutoff = now - SPAWN_RECORD_MAX_AGE_MS;
14828
14913
  const kept = [...merged.entries()].filter(([, entry]) => entry.at >= cutoff).sort((a, b) => a[1].at - b[1].at).slice(-MAX_SPAWN_RECORDS);
14829
14914
  try {
14830
- mkdirSync9(join18(file, ".."), { recursive: true, mode: 448 });
14915
+ mkdirSync9(join19(file, ".."), { recursive: true, mode: 448 });
14831
14916
  const tmp = `${file}.${process.pid}.tmp`;
14832
14917
  writeFileSync10(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {
14833
14918
  mode: 384
@@ -14902,13 +14987,13 @@ function dedupe2(dirs) {
14902
14987
  }
14903
14988
 
14904
14989
  // src/prompts.ts
14905
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
14906
- import { homedir as homedir15 } from "os";
14990
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
14991
+ import { homedir as homedir16 } from "os";
14907
14992
  import { randomUUID as randomUUID7 } from "crypto";
14908
- import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join19 } from "path";
14993
+ import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join20 } from "path";
14909
14994
  function readPromptFileIfPresent(path15) {
14910
14995
  try {
14911
- const content = readFileSync12(path15, "utf8").trim();
14996
+ const content = readFileSync13(path15, "utf8").trim();
14912
14997
  return content || void 0;
14913
14998
  } catch {
14914
14999
  return void 0;
@@ -14917,7 +15002,7 @@ function readPromptFileIfPresent(path15) {
14917
15002
  function nearestWorkspaceAgentsPrompt(cwd) {
14918
15003
  let dir = cwd;
14919
15004
  while (true) {
14920
- const content = readPromptFileIfPresent(join19(dir, "AGENTS.md"));
15005
+ const content = readPromptFileIfPresent(join20(dir, "AGENTS.md"));
14921
15006
  if (content) return content;
14922
15007
  const parent = dirname6(dir);
14923
15008
  if (parent === dir) return void 0;
@@ -15028,7 +15113,7 @@ var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLA
15028
15113
  function userAuthoredInstructions(forwarded, opts = {}) {
15029
15114
  const readFile3 = opts.readFile ?? ((path15) => {
15030
15115
  try {
15031
- return readFileSync12(path15, "utf8");
15116
+ return readFileSync13(path15, "utf8");
15032
15117
  } catch {
15033
15118
  return null;
15034
15119
  }
@@ -15069,8 +15154,8 @@ ${options.compressionSummary.trim()}`
15069
15154
  for (const s of extraSystemContent) {
15070
15155
  if (s.trim()) parts.push(s.trim());
15071
15156
  }
15072
- const configRoot = process.env.XDG_CONFIG_HOME ?? join19(homedir15(), ".config");
15073
- const globalAgents = readPromptFileIfPresent(join19(configRoot, "opencode", "AGENTS.md"));
15157
+ const configRoot = process.env.XDG_CONFIG_HOME ?? join20(homedir16(), ".config");
15158
+ const globalAgents = readPromptFileIfPresent(join20(configRoot, "opencode", "AGENTS.md"));
15074
15159
  const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
15075
15160
  const forwarded = extraSystemContent.join("\n\n");
15076
15161
  const pushGlobal = !!globalAgents && !forwarded.includes(globalAgents);
@@ -15086,7 +15171,7 @@ ${options.compressionSummary.trim()}`
15086
15171
  if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
15087
15172
  const content = parts.join("\n\n");
15088
15173
  if (!content) return void 0;
15089
- const path15 = join19(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
15174
+ const path15 = join20(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
15090
15175
  try {
15091
15176
  writeFileSync11(path15, content, { encoding: "utf8", mode: 384 });
15092
15177
  return path15;
@@ -16098,8 +16183,8 @@ function watchTurnAbort(signal) {
16098
16183
  let aborted = signal?.aborted === true;
16099
16184
  let listeners = [];
16100
16185
  let settle;
16101
- const whenAborted = new Promise((resolve7) => {
16102
- settle = resolve7;
16186
+ const whenAborted = new Promise((resolve8) => {
16187
+ settle = resolve8;
16103
16188
  });
16104
16189
  const fire = () => {
16105
16190
  if (aborted && listeners === null) return;
@@ -16872,7 +16957,8 @@ function createLineHandler(state, ctx) {
16872
16957
  }
16873
16958
  reportFastModeState(msg, ctx.fastMode, "init");
16874
16959
  reportSystemInit(msg, {
16875
- ignoreAnthropicApiKey: ctx.config.ignoreAnthropicApiKey
16960
+ ignoreAnthropicApiKey: ctx.config.ignoreAnthropicApiKey,
16961
+ configDir: ctx.accountConfigDir
16876
16962
  });
16877
16963
  }
16878
16964
  if (msg.type === "system" && msg.subtype === "compact_boundary") {
@@ -18392,7 +18478,15 @@ var ClaudeCodeLanguageModel = class {
18392
18478
  cwd,
18393
18479
  `${effectiveModelId}::${scope}::${affinity}::context=${JSON.stringify(context)}`
18394
18480
  );
18395
- const sk = compactionMode ? sessionKey(cwd, `${effectiveModelId}::compaction::${affinity}`) : effortSessionKey(baseKey, reasoningEffort);
18481
+ const requestedKey = effortSessionKey(baseKey, reasoningEffort);
18482
+ const busyVariant = compactionMode ? void 0 : findBusySpawnVariant(requestedKey);
18483
+ if (busyVariant) {
18484
+ log.notice("finishing the turn in progress under its original settings", {
18485
+ sessionKey: busyVariant,
18486
+ requestedKey
18487
+ });
18488
+ }
18489
+ const sk = compactionMode ? sessionKey(cwd, `${effectiveModelId}::compaction::${affinity}`) : busyVariant ?? requestedKey;
18396
18490
  const toUsage2 = this.toUsage.bind(this);
18397
18491
  const toFinishReason2 = this.toFinishReason.bind(this);
18398
18492
  const handleControlRequest2 = this.handleControlRequest.bind(this);
@@ -18602,14 +18696,20 @@ var ClaudeCodeLanguageModel = class {
18602
18696
  });
18603
18697
  }
18604
18698
  }
18605
- if (!compactionMode) invalidateOtherSpawnVariants(baseKey, reasoningEffort);
18606
- if (!compactionMode && affinity !== "default") {
18607
- recordSessionSpawn(affinity, {
18608
- model: stripAccountSuffix(failover.modelId),
18609
- effort: reasoningEffort,
18610
- account: (this.config.failoverAccounts?.length ?? 0) > 1 ? failover.target ?? sourceAccount : void 0,
18611
- agent: this.getOpencodeAgent(options)
18699
+ if (!compactionMode) {
18700
+ invalidateOtherSpawnVariants(sk, {
18701
+ carryConversation: this.config.resumeAcrossModelChanges !== false
18612
18702
  });
18703
+ }
18704
+ if (!compactionMode && affinity !== "default") {
18705
+ if (!busyVariant) {
18706
+ recordSessionSpawn(affinity, {
18707
+ model: stripAccountSuffix(failover.modelId),
18708
+ effort: reasoningEffort,
18709
+ account: (this.config.failoverAccounts?.length ?? 0) > 1 ? failover.target ?? sourceAccount : void 0,
18710
+ agent: this.getOpencodeAgent(options)
18711
+ });
18712
+ }
18613
18713
  const parked = new Set(getPendingProxyCalls(sk).map((call) => call.toolCallId));
18614
18714
  if (startsTurn(options.prompt, parked)) {
18615
18715
  markTurnStart(sk, {
@@ -18930,7 +19030,7 @@ var ClaudeCodeLanguageModel = class {
18930
19030
  let replayReason = "no-session-for-key";
18931
19031
  if (this.config.resumeAfterRestart !== false && includeHistoryContext && !compactionMode && !forkFromClaudeSessionId && failoverAnswer?.kind !== "switch") {
18932
19032
  const configDir = resolveConfigDir(this.config.configDir);
18933
- const transcriptPath2 = (id, dir) => join20(dir ?? configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
19033
+ const transcriptPath2 = (id, dir) => join21(dir ?? configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
18934
19034
  const resumePoint = findResumePoint({
18935
19035
  sessionKey: sk,
18936
19036
  prompt: options.prompt,
@@ -20414,7 +20514,7 @@ var ClaudeCodeLanguageModel = class {
20414
20514
  return fresh.length;
20415
20515
  };
20416
20516
  if (!state.unattendedTurnEnded && await forwardTrailingUserMessages() > 0) {
20417
- await new Promise((resolve7) => setTimeout(resolve7, FORWARD_SETTLE_MS));
20517
+ await new Promise((resolve8) => setTimeout(resolve8, FORWARD_SETTLE_MS));
20418
20518
  if (state.controllerClosed) return;
20419
20519
  }
20420
20520
  for (const { call, result: opencodeResult } of previousPendingProxyMatches) {
@@ -20518,7 +20618,7 @@ var ClaudeCodeLanguageModel = class {
20518
20618
  };
20519
20619
 
20520
20620
  // src/account-tier.ts
20521
- import { readFileSync as readFileSync13 } from "fs";
20621
+ import { readFileSync as readFileSync14 } from "fs";
20522
20622
  import path14 from "path";
20523
20623
  var ORGANIZATION_TYPE_LABELS = {
20524
20624
  claude_max: "Max",
@@ -20561,7 +20661,7 @@ function readTierFromConfigDir(configDir, allowLegacyFallback) {
20561
20661
  function readOauthAccount(file) {
20562
20662
  let raw;
20563
20663
  try {
20564
- raw = readFileSync13(file, "utf8");
20664
+ raw = readFileSync14(file, "utf8");
20565
20665
  } catch {
20566
20666
  return void 0;
20567
20667
  }
@@ -20599,13 +20699,13 @@ function registerSidebarAccounts(accounts) {
20599
20699
  import {
20600
20700
  existsSync as existsSync5,
20601
20701
  mkdirSync as mkdirSync10,
20602
- readFileSync as readFileSync14,
20702
+ readFileSync as readFileSync15,
20603
20703
  realpathSync as realpathSync4,
20604
20704
  rmSync as rmSync3,
20605
20705
  writeFileSync as writeFileSync12
20606
20706
  } from "fs";
20607
- import { homedir as homedir16 } from "os";
20608
- import { dirname as dirname7, join as join21, resolve as resolve6 } from "path";
20707
+ import { homedir as homedir17 } from "os";
20708
+ import { dirname as dirname7, join as join22, resolve as resolve7 } from "path";
20609
20709
  import { fileURLToPath as fileURLToPath4 } from "url";
20610
20710
  var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
20611
20711
  var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
@@ -20613,20 +20713,20 @@ var alreadyRan = false;
20613
20713
  function candidateCacheRoots() {
20614
20714
  const xdg = process.env.XDG_CACHE_HOME;
20615
20715
  return [
20616
- xdg ? join21(xdg, "opencode") : null,
20617
- join21(homedir16(), ".cache", "opencode"),
20618
- join21(homedir16(), "Library", "Caches", "opencode")
20716
+ xdg ? join22(xdg, "opencode") : null,
20717
+ join22(homedir17(), ".cache", "opencode"),
20718
+ join22(homedir17(), "Library", "Caches", "opencode")
20619
20719
  ].filter((p) => Boolean(p));
20620
20720
  }
20621
20721
  function userOpencodeJsonPath() {
20622
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join21(homedir16(), ".config");
20623
- return join21(xdgConfig, "opencode", "opencode.json");
20722
+ const xdgConfig = process.env.XDG_CONFIG_HOME ?? join22(homedir17(), ".config");
20723
+ return join22(xdgConfig, "opencode", "opencode.json");
20624
20724
  }
20625
20725
  function userIntendsToUseUnscoped() {
20626
20726
  const cfg = userOpencodeJsonPath();
20627
20727
  if (!existsSync5(cfg)) return false;
20628
20728
  try {
20629
- const json = JSON.parse(readFileSync14(cfg, "utf8"));
20729
+ const json = JSON.parse(readFileSync15(cfg, "utf8"));
20630
20730
  const plugins = json.plugin;
20631
20731
  if (!Array.isArray(plugins)) return false;
20632
20732
  return plugins.some(
@@ -20639,7 +20739,7 @@ function userIntendsToUseUnscoped() {
20639
20739
  function ourLoadedDir() {
20640
20740
  try {
20641
20741
  const filePath = fileURLToPath4(import.meta.url);
20642
- return realpathSync4(resolve6(filePath, "..", ".."));
20742
+ return realpathSync4(resolve7(filePath, "..", ".."));
20643
20743
  } catch {
20644
20744
  return null;
20645
20745
  }
@@ -20647,19 +20747,19 @@ function ourLoadedDir() {
20647
20747
  function ourVersion(dir) {
20648
20748
  if (!dir) return null;
20649
20749
  try {
20650
- const pkg = JSON.parse(readFileSync14(join21(dir, "package.json"), "utf8"));
20750
+ const pkg = JSON.parse(readFileSync15(join22(dir, "package.json"), "utf8"));
20651
20751
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
20652
20752
  } catch {
20653
20753
  return null;
20654
20754
  }
20655
20755
  }
20656
20756
  function markerPath() {
20657
- const stateRoot = process.env.XDG_STATE_HOME ?? join21(homedir16(), ".local", "state");
20658
- return join21(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
20757
+ const stateRoot = process.env.XDG_STATE_HOME ?? join22(homedir17(), ".local", "state");
20758
+ return join22(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
20659
20759
  }
20660
20760
  function markerVersion() {
20661
20761
  try {
20662
- const marker = JSON.parse(readFileSync14(markerPath(), "utf8"));
20762
+ const marker = JSON.parse(readFileSync15(markerPath(), "utf8"));
20663
20763
  return typeof marker.version === "string" ? marker.version : null;
20664
20764
  } catch {
20665
20765
  return null;
@@ -20704,7 +20804,7 @@ function cleanupStaleUnscopedInstall() {
20704
20804
  }
20705
20805
  function cleanupOne(cacheRoot, ourDir) {
20706
20806
  if (!existsSync5(cacheRoot)) return;
20707
- const stalePath = join21(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
20807
+ const stalePath = join22(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
20708
20808
  if (!existsSync5(stalePath)) return;
20709
20809
  let realStalePath = stalePath;
20710
20810
  try {
@@ -20712,11 +20812,11 @@ function cleanupOne(cacheRoot, ourDir) {
20712
20812
  } catch {
20713
20813
  }
20714
20814
  if (ourDir && realStalePath === ourDir) return;
20715
- const pkgJsonPath = join21(stalePath, "package.json");
20815
+ const pkgJsonPath = join22(stalePath, "package.json");
20716
20816
  if (!existsSync5(pkgJsonPath)) return;
20717
20817
  let pkg = {};
20718
20818
  try {
20719
- pkg = JSON.parse(readFileSync14(pkgJsonPath, "utf8"));
20819
+ pkg = JSON.parse(readFileSync15(pkgJsonPath, "utf8"));
20720
20820
  } catch {
20721
20821
  return;
20722
20822
  }
@@ -20732,10 +20832,10 @@ function cleanupOne(cacheRoot, ourDir) {
20732
20832
  });
20733
20833
  return;
20734
20834
  }
20735
- const cachePkgJson = join21(cacheRoot, "package.json");
20835
+ const cachePkgJson = join22(cacheRoot, "package.json");
20736
20836
  if (!existsSync5(cachePkgJson)) return;
20737
20837
  try {
20738
- const cfg = JSON.parse(readFileSync14(cachePkgJson, "utf8"));
20838
+ const cfg = JSON.parse(readFileSync15(cachePkgJson, "utf8"));
20739
20839
  if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
20740
20840
  delete cfg.dependencies[STALE_PACKAGE_NAME];
20741
20841
  writeFileSync12(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");