@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 +232 -132
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/claude-code-language-model.ts
|
|
2
|
-
import { join as
|
|
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
|
|
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
|
-
|
|
1328
|
-
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
2886
|
-
import { homedir as
|
|
2887
|
-
import { join as
|
|
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 ||
|
|
2896
|
-
return
|
|
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(
|
|
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(
|
|
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
|
-
(
|
|
5342
|
+
(resolve8, reject2) => {
|
|
5295
5343
|
const entry = {
|
|
5296
5344
|
id: callId,
|
|
5297
5345
|
toolName,
|
|
5298
5346
|
input,
|
|
5299
|
-
resolve:
|
|
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((
|
|
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
|
-
|
|
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((
|
|
5450
|
-
server2.close(() =>
|
|
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((
|
|
5671
|
+
return new Promise((resolve8, reject) => {
|
|
5624
5672
|
const chunks = [];
|
|
5625
5673
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
5626
|
-
req.on("end", () =>
|
|
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((
|
|
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((
|
|
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((
|
|
7105
|
-
timer = setTimeout(() =>
|
|
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
|
|
7316
|
-
import { homedir as
|
|
7317
|
-
import { join as
|
|
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 ||
|
|
7325
|
-
return
|
|
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(
|
|
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(
|
|
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(
|
|
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((
|
|
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
|
-
|
|
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
|
|
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)
|
|
7955
|
+
if (ownServed) join11();
|
|
7908
7956
|
}
|
|
7909
7957
|
function noteRelayMemberServing() {
|
|
7910
7958
|
ownServed = true;
|
|
7911
|
-
|
|
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
|
-
|
|
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((
|
|
8159
|
+
return new Promise((resolve8) => {
|
|
8112
8160
|
const wake = () => {
|
|
8113
8161
|
clearTimeout(timer);
|
|
8114
|
-
|
|
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
|
-
|
|
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((
|
|
8332
|
+
return new Promise((resolve8) => {
|
|
8285
8333
|
const onExit = () => {
|
|
8286
8334
|
clearTimeout(timer);
|
|
8287
|
-
|
|
8335
|
+
resolve8(true);
|
|
8288
8336
|
};
|
|
8289
8337
|
const timer = setTimeout(() => {
|
|
8290
8338
|
proc.off("exit", onExit);
|
|
8291
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
9051
|
+
const inlineDelivered = new Promise((resolve8) => {
|
|
8985
9052
|
markInlineDelivered = () => {
|
|
8986
9053
|
inlineDone = true;
|
|
8987
|
-
|
|
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
|
|
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 ??
|
|
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((
|
|
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
|
|
10301
|
+
if (stdout && stdout.trim()) return resolve8(stdout);
|
|
10234
10302
|
if (error) return reject(error);
|
|
10235
|
-
|
|
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
|
|
10274
|
-
import { homedir as
|
|
10275
|
-
import { join as
|
|
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
|
|
10279
|
-
import { homedir as
|
|
10280
|
-
import { join as
|
|
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 ||
|
|
10288
|
-
return
|
|
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(
|
|
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(
|
|
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 ||
|
|
10337
|
-
return
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
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((
|
|
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 =
|
|
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
|
|
14769
|
-
import { homedir as
|
|
14770
|
-
import { join as
|
|
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 ||
|
|
14778
|
-
return
|
|
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(
|
|
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(
|
|
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
|
|
14906
|
-
import { homedir as
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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 ??
|
|
15073
|
-
const globalAgents = readPromptFileIfPresent(
|
|
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 =
|
|
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((
|
|
16102
|
-
settle =
|
|
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
|
|
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)
|
|
18606
|
-
|
|
18607
|
-
|
|
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) =>
|
|
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((
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
20608
|
-
import { dirname as dirname7, join as
|
|
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 ?
|
|
20617
|
-
|
|
20618
|
-
|
|
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 ??
|
|
20623
|
-
return
|
|
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(
|
|
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(
|
|
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(
|
|
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 ??
|
|
20658
|
-
return
|
|
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(
|
|
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 =
|
|
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 =
|
|
20815
|
+
const pkgJsonPath = join22(stalePath, "package.json");
|
|
20716
20816
|
if (!existsSync5(pkgJsonPath)) return;
|
|
20717
20817
|
let pkg = {};
|
|
20718
20818
|
try {
|
|
20719
|
-
pkg = JSON.parse(
|
|
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 =
|
|
20835
|
+
const cachePkgJson = join22(cacheRoot, "package.json");
|
|
20736
20836
|
if (!existsSync5(cachePkgJson)) return;
|
|
20737
20837
|
try {
|
|
20738
|
-
const cfg = JSON.parse(
|
|
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");
|