@khalilgharbaoui/opencode-claude-code-plugin 0.47.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +16 -0
- package/dist/index.js +215 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +24 -6
package/dist/index.d.ts
CHANGED
|
@@ -382,6 +382,13 @@ interface ClaudeCodeConfig {
|
|
|
382
382
|
* `providerConfig`, not by the user.
|
|
383
383
|
*/
|
|
384
384
|
baseCliPath?: string;
|
|
385
|
+
/**
|
|
386
|
+
* This spawn applies the account itself rather than through a wrapper
|
|
387
|
+
* script: it exports `CLAUDE_CONFIG_DIR` and takes the `@<account>` marker
|
|
388
|
+
* off `--model`. Set by `ensureAccountRuntime` on Windows only, where the
|
|
389
|
+
* wrapper is a shape worth avoiding (h #g221). Not a user option.
|
|
390
|
+
*/
|
|
391
|
+
accountInProcess?: boolean;
|
|
385
392
|
accountFailover?: AccountFailoverMode;
|
|
386
393
|
providerID?: string;
|
|
387
394
|
skipPermissions?: boolean;
|
|
@@ -510,6 +517,11 @@ interface ClaudeCodeProviderSettings {
|
|
|
510
517
|
failoverAccounts?: string[];
|
|
511
518
|
/** The CLI path before the per-account wrapper substitution. */
|
|
512
519
|
baseCliPath?: string;
|
|
520
|
+
/**
|
|
521
|
+
* Written by the config hook on Windows, where there is no wrapper script
|
|
522
|
+
* and the spawn applies the account itself. Not a user option.
|
|
523
|
+
*/
|
|
524
|
+
accountInProcess?: boolean;
|
|
513
525
|
/**
|
|
514
526
|
* When this account is out of usage, show the operator a form listing the
|
|
515
527
|
* other configured accounts and continue the task on the pick, inside the
|
|
@@ -1051,6 +1063,10 @@ interface ClaudeStreamMessage {
|
|
|
1051
1063
|
display_name?: string;
|
|
1052
1064
|
agent_id?: string;
|
|
1053
1065
|
description?: string;
|
|
1066
|
+
/** `elicitation` only: the MCP server that asked for operator input. */
|
|
1067
|
+
mcp_server_name?: string;
|
|
1068
|
+
/** `elicitation` only: `form` or `url`. */
|
|
1069
|
+
mode?: string;
|
|
1054
1070
|
};
|
|
1055
1071
|
/**
|
|
1056
1072
|
* On an `assistant` message the CLI synthesised to report a failure: the
|
package/dist/index.js
CHANGED
|
@@ -154,7 +154,16 @@ function translateStreamForHost(stream, dialect = "v1") {
|
|
|
154
154
|
import { homedir as homedir2 } from "os";
|
|
155
155
|
|
|
156
156
|
// src/accounts.ts
|
|
157
|
-
import {
|
|
157
|
+
import {
|
|
158
|
+
chmod,
|
|
159
|
+
copyFile,
|
|
160
|
+
link,
|
|
161
|
+
lstat,
|
|
162
|
+
mkdir,
|
|
163
|
+
readlink,
|
|
164
|
+
symlink,
|
|
165
|
+
writeFile
|
|
166
|
+
} from "fs/promises";
|
|
158
167
|
import path from "path";
|
|
159
168
|
|
|
160
169
|
// src/logger.ts
|
|
@@ -401,8 +410,9 @@ function resolveAccounts(value) {
|
|
|
401
410
|
function accountProviderId(account) {
|
|
402
411
|
return `${BASE_PROVIDER_ID}-${normalizeAccountName(account)}`;
|
|
403
412
|
}
|
|
404
|
-
function accountDisplayName(account) {
|
|
405
|
-
|
|
413
|
+
function accountDisplayName(account, tier) {
|
|
414
|
+
const name = titleizeAccount(account);
|
|
415
|
+
return `Claude Code (${tier ? `${name}, ${tier}` : name})`;
|
|
406
416
|
}
|
|
407
417
|
function accountModelSuffix(account) {
|
|
408
418
|
const normalized = normalizeAccountName(account);
|
|
@@ -425,13 +435,14 @@ function expandHome(value) {
|
|
|
425
435
|
}
|
|
426
436
|
return value;
|
|
427
437
|
}
|
|
428
|
-
async function ensureAccountRuntime(account, baseCliPath) {
|
|
438
|
+
async function ensureAccountRuntime(account, baseCliPath, deps = {}) {
|
|
439
|
+
const platform2 = deps.platform ?? process.platform;
|
|
429
440
|
const configDir = accountConfigDir(account);
|
|
430
441
|
if (!configDir) return { cliPath: baseCliPath };
|
|
431
442
|
const expandedConfigDir = expandHome(configDir);
|
|
432
443
|
await mkdir(expandedConfigDir, { recursive: true });
|
|
433
444
|
try {
|
|
434
|
-
await ensureSharedCapabilities(expandedConfigDir);
|
|
445
|
+
await ensureSharedCapabilities(expandedConfigDir, platform2);
|
|
435
446
|
} catch (err) {
|
|
436
447
|
log.warn("failed to symlink shared capabilities; continuing anyway", {
|
|
437
448
|
account,
|
|
@@ -439,6 +450,13 @@ async function ensureAccountRuntime(account, baseCliPath) {
|
|
|
439
450
|
error: String(err)
|
|
440
451
|
});
|
|
441
452
|
}
|
|
453
|
+
if (platform2 === "win32") {
|
|
454
|
+
return {
|
|
455
|
+
cliPath: baseCliPath,
|
|
456
|
+
configDir: expandedConfigDir,
|
|
457
|
+
accountInProcess: true
|
|
458
|
+
};
|
|
459
|
+
}
|
|
442
460
|
const cliPath = await writeAccountWrapper(
|
|
443
461
|
normalizeAccountName(account),
|
|
444
462
|
baseCliPath,
|
|
@@ -446,13 +464,21 @@ async function ensureAccountRuntime(account, baseCliPath) {
|
|
|
446
464
|
);
|
|
447
465
|
return { cliPath, configDir: expandedConfigDir };
|
|
448
466
|
}
|
|
449
|
-
async function ensureSharedCapabilities(targetRoot) {
|
|
467
|
+
async function ensureSharedCapabilities(targetRoot, platform2) {
|
|
450
468
|
const sourceRoot = expandHome("~/.claude");
|
|
451
469
|
for (const item of SHARED_CAPABILITY_ITEMS) {
|
|
452
|
-
|
|
470
|
+
try {
|
|
471
|
+
await ensureSharedCapabilityItem(sourceRoot, targetRoot, item, platform2);
|
|
472
|
+
} catch (err) {
|
|
473
|
+
log.warn("could not share a Claude capability with the account", {
|
|
474
|
+
item,
|
|
475
|
+
target: path.join(targetRoot, item),
|
|
476
|
+
error: String(err)
|
|
477
|
+
});
|
|
478
|
+
}
|
|
453
479
|
}
|
|
454
480
|
}
|
|
455
|
-
async function ensureSharedCapabilityItem(sourceRoot, targetRoot, item) {
|
|
481
|
+
async function ensureSharedCapabilityItem(sourceRoot, targetRoot, item, platform2) {
|
|
456
482
|
const source = path.join(sourceRoot, item);
|
|
457
483
|
const target = path.join(targetRoot, item);
|
|
458
484
|
let sourceStat;
|
|
@@ -477,8 +503,29 @@ async function ensureSharedCapabilityItem(sourceRoot, targetRoot, item) {
|
|
|
477
503
|
return;
|
|
478
504
|
} catch {
|
|
479
505
|
}
|
|
480
|
-
|
|
481
|
-
|
|
506
|
+
if (sourceStat.isDirectory()) {
|
|
507
|
+
await symlink(source, target, platform2 === "win32" ? "junction" : "dir");
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (platform2 !== "win32") {
|
|
511
|
+
await symlink(source, target, "file");
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
await symlink(source, target, "file");
|
|
516
|
+
return;
|
|
517
|
+
} catch {
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
await link(source, target);
|
|
521
|
+
return;
|
|
522
|
+
} catch {
|
|
523
|
+
}
|
|
524
|
+
await copyFile(source, target);
|
|
525
|
+
log.warn(
|
|
526
|
+
"copied a shared Claude capability into the account config dir instead of linking it; later edits to the original will not be picked up",
|
|
527
|
+
{ item, target }
|
|
528
|
+
);
|
|
482
529
|
}
|
|
483
530
|
async function writeAccountWrapper(account, baseCliPath, configDir) {
|
|
484
531
|
const cacheRoot = path.join(
|
|
@@ -5067,6 +5114,9 @@ function claudeSpawnEnv(opts) {
|
|
|
5067
5114
|
if (opts?.promptCacheTtl) {
|
|
5068
5115
|
env.CLAUDE_CODE_PROMPT_CACHE_TTL = opts.promptCacheTtl;
|
|
5069
5116
|
}
|
|
5117
|
+
if (opts?.configDir) {
|
|
5118
|
+
env.CLAUDE_CONFIG_DIR = opts.configDir;
|
|
5119
|
+
}
|
|
5070
5120
|
if (opts?.ignoreAnthropicApiKey) {
|
|
5071
5121
|
delete env.ANTHROPIC_API_KEY;
|
|
5072
5122
|
delete env.ANTHROPIC_AUTH_TOKEN;
|
|
@@ -5184,6 +5234,11 @@ function getActiveProcess(key) {
|
|
|
5184
5234
|
}
|
|
5185
5235
|
return ap;
|
|
5186
5236
|
}
|
|
5237
|
+
function processBelongsToAnotherAccount(active, cliPath, configDir) {
|
|
5238
|
+
if (!active) return false;
|
|
5239
|
+
if (active.cliPath && active.cliPath !== cliPath) return true;
|
|
5240
|
+
return !!active.configDir && active.configDir !== configDir;
|
|
5241
|
+
}
|
|
5187
5242
|
function setActiveProcess(key, ap) {
|
|
5188
5243
|
cancelIdleProcessEviction(key);
|
|
5189
5244
|
activeProcesses.set(key, ap);
|
|
@@ -5399,7 +5454,7 @@ function invalidateOtherEffortSessions(baseKey, effort) {
|
|
|
5399
5454
|
clearBackgroundTasks(key);
|
|
5400
5455
|
}
|
|
5401
5456
|
}
|
|
5402
|
-
function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcpHash, systemPromptFile, ignoreAnthropicApiKey, effort, promptCacheTtl) {
|
|
5457
|
+
function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcpHash, systemPromptFile, ignoreAnthropicApiKey, effort, promptCacheTtl, configDir) {
|
|
5403
5458
|
evictIfNeeded();
|
|
5404
5459
|
log.info("spawning new claude process", {
|
|
5405
5460
|
cliPath,
|
|
@@ -5407,13 +5462,14 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
|
|
|
5407
5462
|
cwd,
|
|
5408
5463
|
sessionKey: sessionKey2,
|
|
5409
5464
|
effort,
|
|
5410
|
-
promptCacheTtl
|
|
5465
|
+
promptCacheTtl,
|
|
5466
|
+
configDir
|
|
5411
5467
|
});
|
|
5412
5468
|
const plan = planClaudeSpawn(cliPath, cliArgs);
|
|
5413
5469
|
const proc = spawn(plan.file, plan.args, {
|
|
5414
5470
|
cwd,
|
|
5415
5471
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5416
|
-
env: claudeSpawnEnv({ ignoreAnthropicApiKey, effort, promptCacheTtl }),
|
|
5472
|
+
env: claudeSpawnEnv({ ignoreAnthropicApiKey, effort, promptCacheTtl, configDir }),
|
|
5417
5473
|
windowsVerbatimArguments: plan.windowsVerbatimArguments
|
|
5418
5474
|
});
|
|
5419
5475
|
const lineEmitter = new EventEmitter3();
|
|
@@ -5428,6 +5484,7 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
|
|
|
5428
5484
|
startedAt: Date.now(),
|
|
5429
5485
|
cliPath,
|
|
5430
5486
|
cliArgs: [...cliArgs],
|
|
5487
|
+
configDir,
|
|
5431
5488
|
unattendedLines: [],
|
|
5432
5489
|
unattendedDropped: 0
|
|
5433
5490
|
};
|
|
@@ -5553,7 +5610,8 @@ function respawnActiveProcess(sessionKey2, cliPath, cliArgs, cwd, ignoreAnthropi
|
|
|
5553
5610
|
old.systemPromptFile,
|
|
5554
5611
|
ignoreAnthropicApiKey,
|
|
5555
5612
|
old.effort,
|
|
5556
|
-
old.promptCacheTtl
|
|
5613
|
+
old.promptCacheTtl,
|
|
5614
|
+
old.configDir
|
|
5557
5615
|
);
|
|
5558
5616
|
replacement.mcpServers = old.mcpServers;
|
|
5559
5617
|
replacement.pendingProxyCompletions = old.pendingProxyCompletions;
|
|
@@ -5984,6 +6042,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
5984
6042
|
"ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN detected; stripping it from claude spawns (ignoreAnthropicApiKey) so requests use your subscription auth, not pay-as-you-go API billing.",
|
|
5985
6043
|
"Code Mode execute not forwarded: no model-visible input schema",
|
|
5986
6044
|
"Code Mode execute not forwarded: read-only permission preset",
|
|
6045
|
+
"MCP server asked for operator input and was declined: a headless Claude Code session cannot prompt, so its elicitation can only be refused. Run that server's flow in Claude Code directly, or configure it not to elicit.",
|
|
5987
6046
|
"MCP tool not routed through the proxy: another proxy tool already holds that name, and it keeps it",
|
|
5988
6047
|
"account failover declined; ending the turn",
|
|
5989
6048
|
"agent effort override refused: unknown level",
|
|
@@ -6020,6 +6079,8 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
6020
6079
|
"cleanup-stale: error processing cache root",
|
|
6021
6080
|
"cleanup-stale: rmSync failed",
|
|
6022
6081
|
"continuing this conversation's claude session under the new model or effort instead of replaying it",
|
|
6082
|
+
"copied a shared Claude capability into the account config dir instead of linking it; later edits to the original will not be picked up",
|
|
6083
|
+
"could not share a Claude capability with the account",
|
|
6023
6084
|
"delivering proxy results after interrupted continuation",
|
|
6024
6085
|
"draining proxy calls queued between turns",
|
|
6025
6086
|
"empty user content; sending sentinel to satisfy CLI",
|
|
@@ -6208,6 +6269,8 @@ var BUNDLE_DATA_ALLOWLIST = {
|
|
|
6208
6269
|
finishReason: "enum",
|
|
6209
6270
|
hash: "enum",
|
|
6210
6271
|
hostApi: "enum",
|
|
6272
|
+
// One of `SHARED_CAPABILITY_ITEMS`: a fixed list this package defines.
|
|
6273
|
+
item: "enum",
|
|
6211
6274
|
kind: "enum",
|
|
6212
6275
|
level: "enum",
|
|
6213
6276
|
mode: "enum",
|
|
@@ -6517,8 +6580,8 @@ function redactLogLine(raw, context) {
|
|
|
6517
6580
|
if (!parsed.data || Object.keys(parsed.data).length === 0) return head;
|
|
6518
6581
|
return `${head} ${JSON.stringify(sanitizeLogData(parsed.data, context))}`;
|
|
6519
6582
|
}
|
|
6520
|
-
function readLogTail(
|
|
6521
|
-
const fd = openSync(
|
|
6583
|
+
function readLogTail(path14, maxBytes = BUNDLE_TAIL_BYTES) {
|
|
6584
|
+
const fd = openSync(path14, "r");
|
|
6522
6585
|
try {
|
|
6523
6586
|
const size = fstatSync(fd).size;
|
|
6524
6587
|
const length = Math.min(size, maxBytes);
|
|
@@ -6698,7 +6761,10 @@ async function fetchPlanUsage(cliPath, options = {}) {
|
|
|
6698
6761
|
if (support && await support(cliPath) === "unsupported") {
|
|
6699
6762
|
return { status: "failed", error: "Plan usage is unavailable: this Claude CLI does not support headless output. Check usage in the Claude CLI directly." };
|
|
6700
6763
|
}
|
|
6701
|
-
const env = claudeSpawnEnv({
|
|
6764
|
+
const env = claudeSpawnEnv({
|
|
6765
|
+
ignoreAnthropicApiKey: options.ignoreAnthropicApiKey,
|
|
6766
|
+
configDir: options.configDir
|
|
6767
|
+
});
|
|
6702
6768
|
const stdout = await run(
|
|
6703
6769
|
cliPath,
|
|
6704
6770
|
["-p", "/cost", "--output-format", "json"],
|
|
@@ -7369,7 +7435,8 @@ async function gatherDoctorReport(options) {
|
|
|
7369
7435
|
});
|
|
7370
7436
|
}
|
|
7371
7437
|
const planUsage = wantsPlanUsage(options.argument ?? "") ? await (options.planUsageImpl ?? fetchPlanUsage)(cliPath, {
|
|
7372
|
-
ignoreAnthropicApiKey: options.ignoreAnthropicApiKey
|
|
7438
|
+
ignoreAnthropicApiKey: options.ignoreAnthropicApiKey,
|
|
7439
|
+
configDir: options.configDir
|
|
7373
7440
|
}) : { status: "not-requested" };
|
|
7374
7441
|
return {
|
|
7375
7442
|
plugin: base.plugin,
|
|
@@ -10652,7 +10719,7 @@ function spawnInteractiveProcess(opts) {
|
|
|
10652
10719
|
// src/account-transcript.ts
|
|
10653
10720
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
10654
10721
|
import { constants as fsConstants } from "fs";
|
|
10655
|
-
import { chmod as chmod2, copyFile, lstat as lstat2, mkdir as mkdir2 } from "fs/promises";
|
|
10722
|
+
import { chmod as chmod2, copyFile as copyFile2, lstat as lstat2, mkdir as mkdir2 } from "fs/promises";
|
|
10656
10723
|
import path11 from "path";
|
|
10657
10724
|
var transcriptPath = interactiveTranscriptPath;
|
|
10658
10725
|
function configDirForAccount(account) {
|
|
@@ -10682,7 +10749,7 @@ async function carryTranscriptToAccount(input) {
|
|
|
10682
10749
|
const target = renamed ? transcriptPath({ configDir: targetConfigDir, cwd, sessionId: carriedId }) : targetOwn;
|
|
10683
10750
|
try {
|
|
10684
10751
|
await mkdir2(path11.dirname(target), { recursive: true, mode: 448 });
|
|
10685
|
-
await
|
|
10752
|
+
await copyFile2(live.file, target, fsConstants.COPYFILE_EXCL);
|
|
10686
10753
|
await chmod2(target, 384);
|
|
10687
10754
|
} catch (err) {
|
|
10688
10755
|
return { kind: "refused", reason: "copy-failed", detail: String(err) };
|
|
@@ -10714,9 +10781,9 @@ import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "
|
|
|
10714
10781
|
import { homedir as homedir8 } from "os";
|
|
10715
10782
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
10716
10783
|
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join11 } from "path";
|
|
10717
|
-
function readPromptFileIfPresent(
|
|
10784
|
+
function readPromptFileIfPresent(path14) {
|
|
10718
10785
|
try {
|
|
10719
|
-
const content = readFileSync6(
|
|
10786
|
+
const content = readFileSync6(path14, "utf8").trim();
|
|
10720
10787
|
return content || void 0;
|
|
10721
10788
|
} catch {
|
|
10722
10789
|
return void 0;
|
|
@@ -10821,9 +10888,9 @@ function extractSystemMessages(prompt) {
|
|
|
10821
10888
|
}
|
|
10822
10889
|
var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLAUDE.local.md"]);
|
|
10823
10890
|
function userAuthoredInstructions(forwarded, opts = {}) {
|
|
10824
|
-
const readFile2 = opts.readFile ?? ((
|
|
10891
|
+
const readFile2 = opts.readFile ?? ((path14) => {
|
|
10825
10892
|
try {
|
|
10826
|
-
return readFileSync6(
|
|
10893
|
+
return readFileSync6(path14, "utf8");
|
|
10827
10894
|
} catch {
|
|
10828
10895
|
return null;
|
|
10829
10896
|
}
|
|
@@ -10835,16 +10902,16 @@ function userAuthoredInstructions(forwarded, opts = {}) {
|
|
|
10835
10902
|
}
|
|
10836
10903
|
const seen = /* @__PURE__ */ new Set();
|
|
10837
10904
|
for (const match of forwarded.matchAll(/^Instructions from: (.+)$/gm)) {
|
|
10838
|
-
const
|
|
10839
|
-
if (seen.has(
|
|
10840
|
-
if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(
|
|
10841
|
-
seen.add(
|
|
10842
|
-
const file = readFile2(
|
|
10905
|
+
const path14 = match[1].trim();
|
|
10906
|
+
if (seen.has(path14) || !isAbsolute3(path14)) continue;
|
|
10907
|
+
if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path14))) continue;
|
|
10908
|
+
seen.add(path14);
|
|
10909
|
+
const file = readFile2(path14);
|
|
10843
10910
|
if (!file?.trim()) continue;
|
|
10844
10911
|
const start = match.index + match[0].length + 1;
|
|
10845
10912
|
const content = forwarded.startsWith(file, start) ? file : forwarded.startsWith(file.trimEnd(), start) ? file.trimEnd() : null;
|
|
10846
10913
|
if (content === null) continue;
|
|
10847
|
-
out.push({ text: `Instructions from: ${
|
|
10914
|
+
out.push({ text: `Instructions from: ${path14}
|
|
10848
10915
|
${content}`, content });
|
|
10849
10916
|
}
|
|
10850
10917
|
return out;
|
|
@@ -10881,10 +10948,10 @@ ${options.compressionSummary.trim()}`
|
|
|
10881
10948
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
10882
10949
|
const content = parts.join("\n\n");
|
|
10883
10950
|
if (!content) return void 0;
|
|
10884
|
-
const
|
|
10951
|
+
const path14 = join11(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
|
|
10885
10952
|
try {
|
|
10886
|
-
writeFileSync6(
|
|
10887
|
-
return
|
|
10953
|
+
writeFileSync6(path14, content, { encoding: "utf8", mode: 384 });
|
|
10954
|
+
return path14;
|
|
10888
10955
|
} catch (err) {
|
|
10889
10956
|
log.warn("failed to write system prompt file", { error: String(err) });
|
|
10890
10957
|
return void 0;
|
|
@@ -11215,6 +11282,8 @@ _Reply with your answer to continue._
|
|
|
11215
11282
|
}
|
|
11216
11283
|
|
|
11217
11284
|
// src/control-request.ts
|
|
11285
|
+
var ELICITATION_DECLINE = { action: "decline" };
|
|
11286
|
+
var warnedElicitationServers = /* @__PURE__ */ new Set();
|
|
11218
11287
|
function controlRequestBehaviorForTool(config, toolName) {
|
|
11219
11288
|
const configured = config.controlRequestToolBehaviors;
|
|
11220
11289
|
if (configured && toolName) {
|
|
@@ -11283,6 +11352,18 @@ function handleControlRequest(config, msg, proc) {
|
|
|
11283
11352
|
}
|
|
11284
11353
|
return true;
|
|
11285
11354
|
}
|
|
11355
|
+
if (request.subtype === "elicitation") {
|
|
11356
|
+
const server2 = request.mcp_server_name || "unknown";
|
|
11357
|
+
writeControlResponse(proc, requestId, { ...ELICITATION_DECLINE });
|
|
11358
|
+
if (!warnedElicitationServers.has(server2)) {
|
|
11359
|
+
warnedElicitationServers.add(server2);
|
|
11360
|
+
log.warn(
|
|
11361
|
+
"MCP server asked for operator input and was declined: a headless Claude Code session cannot prompt, so its elicitation can only be refused. Run that server's flow in Claude Code directly, or configure it not to elicit.",
|
|
11362
|
+
{ server: server2, mode: request.mode ?? "form" }
|
|
11363
|
+
);
|
|
11364
|
+
}
|
|
11365
|
+
return true;
|
|
11366
|
+
}
|
|
11286
11367
|
writeControlResponse(proc, requestId, {});
|
|
11287
11368
|
log.debug("control request acknowledged", {
|
|
11288
11369
|
requestId,
|
|
@@ -12001,14 +12082,18 @@ var FAST_MODE_REASONS = {
|
|
|
12001
12082
|
pending: "the CLI is still checking availability"
|
|
12002
12083
|
};
|
|
12003
12084
|
var warnedFastModeReasons = /* @__PURE__ */ new Set();
|
|
12004
|
-
function reportFastModeState(msg, requested) {
|
|
12085
|
+
function reportFastModeState(msg, requested, phase = "result") {
|
|
12005
12086
|
const state = msg.fast_mode_state;
|
|
12006
12087
|
if (!state) return;
|
|
12007
12088
|
if (!requested) {
|
|
12008
|
-
log.debug("fast mode state", { state });
|
|
12089
|
+
log.debug("fast mode state", { state, phase });
|
|
12009
12090
|
return;
|
|
12010
12091
|
}
|
|
12011
12092
|
if (state === "on") {
|
|
12093
|
+
if (phase === "init") {
|
|
12094
|
+
log.debug("fast mode reported on at session start", { state, phase });
|
|
12095
|
+
return;
|
|
12096
|
+
}
|
|
12012
12097
|
log.info("fast mode active", { state });
|
|
12013
12098
|
return;
|
|
12014
12099
|
}
|
|
@@ -12503,7 +12588,7 @@ function createLineHandler(state, ctx) {
|
|
|
12503
12588
|
claudeSessionId: msg.session_id
|
|
12504
12589
|
});
|
|
12505
12590
|
}
|
|
12506
|
-
reportFastModeState(msg, ctx.fastMode);
|
|
12591
|
+
reportFastModeState(msg, ctx.fastMode, "init");
|
|
12507
12592
|
reportSystemInit(msg, {
|
|
12508
12593
|
ignoreAnthropicApiKey: ctx.config.ignoreAnthropicApiKey
|
|
12509
12594
|
});
|
|
@@ -13069,6 +13154,7 @@ ${plan}
|
|
|
13069
13154
|
}
|
|
13070
13155
|
if (msg.type === "result") {
|
|
13071
13156
|
clearFallbackTimer(state);
|
|
13157
|
+
reportFastModeState(msg, ctx.fastMode, "result");
|
|
13072
13158
|
if (msg.session_id) {
|
|
13073
13159
|
setClaudeSessionId(state.sessionKey, msg.session_id);
|
|
13074
13160
|
}
|
|
@@ -13691,6 +13777,11 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13691
13777
|
interactive: !!useInteractive,
|
|
13692
13778
|
turnStats: this.config.turnStats === true,
|
|
13693
13779
|
ignoreAnthropicApiKey: this.config.ignoreAnthropicApiKey === true,
|
|
13780
|
+
// Where the plugin applies the account itself rather than through a
|
|
13781
|
+
// wrapper (`accountInProcess`, Windows), the `/cost` spawn needs the
|
|
13782
|
+
// same config dir a turn gets, or it reports the default account's
|
|
13783
|
+
// plan usage instead of this provider's.
|
|
13784
|
+
configDir: this.config.accountInProcess ? this.config.configDir : void 0,
|
|
13694
13785
|
// `/claude-code-doctor usage` opts into the CLI's own plan-usage
|
|
13695
13786
|
// report; anything else here is ignored, as it always has been.
|
|
13696
13787
|
argument: doctor.rest
|
|
@@ -13891,11 +13982,12 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13891
13982
|
resetsAt: failoverAnswer.resetsAt
|
|
13892
13983
|
});
|
|
13893
13984
|
}
|
|
13985
|
+
const spawnConfigDir = this.config.accountInProcess ? this.skillBridgeSpawn(failover).configDir : void 0;
|
|
13894
13986
|
const processForAccount = getActiveProcess(sk);
|
|
13895
|
-
if (!compactionMode && processForAccount
|
|
13987
|
+
if (!compactionMode && processBelongsToAnotherAccount(processForAccount, cliPath, spawnConfigDir)) {
|
|
13896
13988
|
log.notice("claude process belongs to another account; starting fresh", {
|
|
13897
13989
|
sessionKey: sk,
|
|
13898
|
-
was: processForAccount
|
|
13990
|
+
was: processForAccount?.cliPath,
|
|
13899
13991
|
now: cliPath,
|
|
13900
13992
|
failedOver: failover.failedOver
|
|
13901
13993
|
});
|
|
@@ -13938,7 +14030,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13938
14030
|
});
|
|
13939
14031
|
deleteClaudeSessionId(sk);
|
|
13940
14032
|
}
|
|
13941
|
-
} else if (!compactionMode && sessionBeforeAccountCheck && processForAccount
|
|
14033
|
+
} else if (!compactionMode && sessionBeforeAccountCheck && processBelongsToAnotherAccount(processForAccount, cliPath, spawnConfigDir)) {
|
|
13942
14034
|
deleteClaudeSessionId(sk);
|
|
13943
14035
|
}
|
|
13944
14036
|
const hasExistingSession = !!getClaudeSessionId(sk);
|
|
@@ -14051,7 +14143,9 @@ var ClaudeCodeLanguageModel = class {
|
|
|
14051
14143
|
if (this.config.forkSessions === true && !compactionMode) {
|
|
14052
14144
|
recordForkFingerprint(sk, options.prompt, cliPath);
|
|
14053
14145
|
}
|
|
14054
|
-
const { model: spawnModelId, fast: fastMode } = parseModelId(
|
|
14146
|
+
const { model: spawnModelId, fast: fastMode } = parseModelId(
|
|
14147
|
+
this.config.accountInProcess ? stripAccountSuffix(failover.modelId) : failover.modelId
|
|
14148
|
+
);
|
|
14055
14149
|
const exitPlanModeQuestionResult = compactionMode ? null : consumeExitPlanModeQuestionResult(sk, options.prompt);
|
|
14056
14150
|
if (exitPlanModeQuestionResult) {
|
|
14057
14151
|
log.info("sending plan approval decision to claude", { sk });
|
|
@@ -14681,7 +14775,8 @@ var ClaudeCodeLanguageModel = class {
|
|
|
14681
14775
|
spawnSystemPromptFile,
|
|
14682
14776
|
self.config.ignoreAnthropicApiKey,
|
|
14683
14777
|
reasoningEffort,
|
|
14684
|
-
promptCacheTtl
|
|
14778
|
+
promptCacheTtl,
|
|
14779
|
+
spawnConfigDir
|
|
14685
14780
|
);
|
|
14686
14781
|
ap.mcpServers = spawnMcpServers;
|
|
14687
14782
|
state.proc = ap.proc;
|
|
@@ -15242,11 +15337,80 @@ var ClaudeCodeLanguageModel = class {
|
|
|
15242
15337
|
}
|
|
15243
15338
|
};
|
|
15244
15339
|
|
|
15340
|
+
// src/account-tier.ts
|
|
15341
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
15342
|
+
import path12 from "path";
|
|
15343
|
+
var ORGANIZATION_TYPE_LABELS = {
|
|
15344
|
+
claude_max: "Max",
|
|
15345
|
+
claude_pro: "Pro",
|
|
15346
|
+
claude_team: "Team",
|
|
15347
|
+
claude_enterprise: "Enterprise",
|
|
15348
|
+
claude_free: "Free"
|
|
15349
|
+
};
|
|
15350
|
+
var RATE_LIMIT_TIER_LABELS = {
|
|
15351
|
+
default_claude_max_5x: "5x",
|
|
15352
|
+
default_claude_max_20x: "20x"
|
|
15353
|
+
};
|
|
15354
|
+
var tierCache = /* @__PURE__ */ new Map();
|
|
15355
|
+
function accountTierConfigDir(account) {
|
|
15356
|
+
const configured = accountConfigDirPath(account);
|
|
15357
|
+
if (configured) return configured;
|
|
15358
|
+
const fromEnv = process.env.CLAUDE_CONFIG_DIR;
|
|
15359
|
+
return fromEnv && fromEnv.trim() ? expandHome(fromEnv.trim()) : expandHome("~/.claude");
|
|
15360
|
+
}
|
|
15361
|
+
function readAccountTier(account) {
|
|
15362
|
+
const normalized = normalizeAccountName(account) || DEFAULT_ACCOUNT;
|
|
15363
|
+
const configDir = accountTierConfigDir(normalized);
|
|
15364
|
+
const cached = tierCache.get(configDir);
|
|
15365
|
+
if (cached !== void 0) return cached ?? void 0;
|
|
15366
|
+
const label = readTierFromConfigDir(configDir, normalized === DEFAULT_ACCOUNT);
|
|
15367
|
+
tierCache.set(configDir, label ?? null);
|
|
15368
|
+
return label;
|
|
15369
|
+
}
|
|
15370
|
+
function readTierFromConfigDir(configDir, allowLegacyFallback) {
|
|
15371
|
+
const candidates = [path12.join(configDir, ".claude.json")];
|
|
15372
|
+
if (allowLegacyFallback) candidates.push(expandHome("~/.claude.json"));
|
|
15373
|
+
for (const candidate of candidates) {
|
|
15374
|
+
const oauth = readOauthAccount(candidate);
|
|
15375
|
+
if (!oauth) continue;
|
|
15376
|
+
const label = tierLabel(oauth);
|
|
15377
|
+
if (label) return label;
|
|
15378
|
+
}
|
|
15379
|
+
return void 0;
|
|
15380
|
+
}
|
|
15381
|
+
function readOauthAccount(file) {
|
|
15382
|
+
let raw;
|
|
15383
|
+
try {
|
|
15384
|
+
raw = readFileSync7(file, "utf8");
|
|
15385
|
+
} catch {
|
|
15386
|
+
return void 0;
|
|
15387
|
+
}
|
|
15388
|
+
try {
|
|
15389
|
+
const parsed = JSON.parse(raw);
|
|
15390
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
15391
|
+
const oauth = parsed.oauthAccount;
|
|
15392
|
+
return oauth && typeof oauth === "object" ? oauth : void 0;
|
|
15393
|
+
} catch {
|
|
15394
|
+
log.debug("could not parse a Claude config file for its plan tier", { file });
|
|
15395
|
+
return void 0;
|
|
15396
|
+
}
|
|
15397
|
+
}
|
|
15398
|
+
function tierLabel(oauthAccount) {
|
|
15399
|
+
const organizationType = oauthAccount.organizationType;
|
|
15400
|
+
if (typeof organizationType !== "string") return void 0;
|
|
15401
|
+
const base = ORGANIZATION_TYPE_LABELS[organizationType];
|
|
15402
|
+
if (!base) return void 0;
|
|
15403
|
+
if (base !== "Max") return base;
|
|
15404
|
+
const rateLimitTier = oauthAccount.organizationRateLimitTier;
|
|
15405
|
+
const multiple = typeof rateLimitTier === "string" ? RATE_LIMIT_TIER_LABELS[rateLimitTier] : void 0;
|
|
15406
|
+
return multiple ? `${base} ${multiple}` : base;
|
|
15407
|
+
}
|
|
15408
|
+
|
|
15245
15409
|
// src/cleanup-stale.ts
|
|
15246
15410
|
import {
|
|
15247
15411
|
existsSync as existsSync5,
|
|
15248
15412
|
mkdirSync as mkdirSync5,
|
|
15249
|
-
readFileSync as
|
|
15413
|
+
readFileSync as readFileSync8,
|
|
15250
15414
|
realpathSync as realpathSync4,
|
|
15251
15415
|
rmSync as rmSync3,
|
|
15252
15416
|
writeFileSync as writeFileSync7
|
|
@@ -15273,7 +15437,7 @@ function userIntendsToUseUnscoped() {
|
|
|
15273
15437
|
const cfg = userOpencodeJsonPath();
|
|
15274
15438
|
if (!existsSync5(cfg)) return false;
|
|
15275
15439
|
try {
|
|
15276
|
-
const json = JSON.parse(
|
|
15440
|
+
const json = JSON.parse(readFileSync8(cfg, "utf8"));
|
|
15277
15441
|
const plugins = json.plugin;
|
|
15278
15442
|
if (!Array.isArray(plugins)) return false;
|
|
15279
15443
|
return plugins.some(
|
|
@@ -15294,7 +15458,7 @@ function ourLoadedDir() {
|
|
|
15294
15458
|
function ourVersion(dir) {
|
|
15295
15459
|
if (!dir) return null;
|
|
15296
15460
|
try {
|
|
15297
|
-
const pkg = JSON.parse(
|
|
15461
|
+
const pkg = JSON.parse(readFileSync8(join13(dir, "package.json"), "utf8"));
|
|
15298
15462
|
return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
|
|
15299
15463
|
} catch {
|
|
15300
15464
|
return null;
|
|
@@ -15306,7 +15470,7 @@ function markerPath() {
|
|
|
15306
15470
|
}
|
|
15307
15471
|
function markerVersion() {
|
|
15308
15472
|
try {
|
|
15309
|
-
const marker = JSON.parse(
|
|
15473
|
+
const marker = JSON.parse(readFileSync8(markerPath(), "utf8"));
|
|
15310
15474
|
return typeof marker.version === "string" ? marker.version : null;
|
|
15311
15475
|
} catch {
|
|
15312
15476
|
return null;
|
|
@@ -15363,7 +15527,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
15363
15527
|
if (!existsSync5(pkgJsonPath)) return;
|
|
15364
15528
|
let pkg = {};
|
|
15365
15529
|
try {
|
|
15366
|
-
pkg = JSON.parse(
|
|
15530
|
+
pkg = JSON.parse(readFileSync8(pkgJsonPath, "utf8"));
|
|
15367
15531
|
} catch {
|
|
15368
15532
|
return;
|
|
15369
15533
|
}
|
|
@@ -15382,7 +15546,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
15382
15546
|
const cachePkgJson = join13(cacheRoot, "package.json");
|
|
15383
15547
|
if (!existsSync5(cachePkgJson)) return;
|
|
15384
15548
|
try {
|
|
15385
|
-
const cfg = JSON.parse(
|
|
15549
|
+
const cfg = JSON.parse(readFileSync8(cachePkgJson, "utf8"));
|
|
15386
15550
|
if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
|
|
15387
15551
|
delete cfg.dependencies[STALE_PACKAGE_NAME];
|
|
15388
15552
|
writeFileSync7(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
|
|
@@ -15462,7 +15626,7 @@ function registerOpencodeLogSink(client) {
|
|
|
15462
15626
|
}
|
|
15463
15627
|
|
|
15464
15628
|
// src/v2-client.ts
|
|
15465
|
-
import
|
|
15629
|
+
import path13 from "path";
|
|
15466
15630
|
function formatAgentTypeList(agents) {
|
|
15467
15631
|
const lines = agents.flatMap((agent) => {
|
|
15468
15632
|
if (agent.hidden === true || agent.mode === "primary") return [];
|
|
@@ -15491,7 +15655,7 @@ function toV1Session(info) {
|
|
|
15491
15655
|
if (!info || typeof info !== "object") return void 0;
|
|
15492
15656
|
const session = info;
|
|
15493
15657
|
const root = session.location?.directory;
|
|
15494
|
-
const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ?
|
|
15658
|
+
const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path13.resolve(root, session.subpath) : root : void 0;
|
|
15495
15659
|
return { ...info, directory, parentID: session.parentID };
|
|
15496
15660
|
}
|
|
15497
15661
|
function toV1Messages(messages) {
|
|
@@ -15683,7 +15847,7 @@ function planV2Providers(seedSettings, defaultProxyTools) {
|
|
|
15683
15847
|
return {
|
|
15684
15848
|
info: {
|
|
15685
15849
|
id: providerID,
|
|
15686
|
-
name: accountDisplayName(account),
|
|
15850
|
+
name: accountDisplayName(account, readAccountTier(account)),
|
|
15687
15851
|
activation: "enabled",
|
|
15688
15852
|
package: V2_PLUGIN_PACKAGE,
|
|
15689
15853
|
settings: {
|
|
@@ -16221,7 +16385,7 @@ async function expandAccountProviders(config) {
|
|
|
16221
16385
|
// stripped by cleanProviderOptions.
|
|
16222
16386
|
failoverAccounts: accounts
|
|
16223
16387
|
},
|
|
16224
|
-
accountDisplayName(account)
|
|
16388
|
+
accountDisplayName(account, readAccountTier(account))
|
|
16225
16389
|
),
|
|
16226
16390
|
models: configModelsForProvider(
|
|
16227
16391
|
existing?.models ?? seed?.models ?? {},
|