@khalilgharbaoui/opencode-claude-code-plugin 0.60.0 → 0.60.2
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 +12 -12
- package/dist/index.js +232 -64
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +84 -13
package/dist/index.js
CHANGED
|
@@ -1998,17 +1998,21 @@ function describeReset(resetsAt) {
|
|
|
1998
1998
|
}
|
|
1999
1999
|
function createAccountFailoverQuestionCall(sessionKey2, input, toolCallId = `${ACCOUNT_FAILOVER_TOOL_CALL_PREFIX}${Math.random().toString(36).slice(2, 10)}`) {
|
|
2000
2000
|
const source = normalizeAccountName(input.sourceAccount || DEFAULT_ACCOUNT);
|
|
2001
|
+
const failed = normalizeAccountName(input.failedAccount || source);
|
|
2001
2002
|
const candidates = input.candidates.map((c) => normalizeAccountName(c));
|
|
2002
2003
|
const resets = describeReset(input.resetsAt);
|
|
2003
2004
|
const until = resets ?? "opencode restarts";
|
|
2004
|
-
const question = input.reason ? `The Claude account "${
|
|
2005
|
-
`The Claude account "${
|
|
2005
|
+
const question = input.reason ? `The Claude account "${failed}" ${input.reason}. Continue this task on another configured account? Leaving this unanswered waits, at no cost.` : [
|
|
2006
|
+
`The Claude account "${failed}" is out of usage`,
|
|
2006
2007
|
input.window ? ` in ${input.window}` : "",
|
|
2007
2008
|
resets ? `, which resets at ${resets}` : "",
|
|
2008
2009
|
". Continue this task on another configured account? Leaving this unanswered waits, at no cost."
|
|
2009
2010
|
].join("");
|
|
2010
2011
|
pendingQuestions2.set(pendingKey2(sessionKey2, toolCallId), {
|
|
2011
2012
|
sourceAccount: source,
|
|
2013
|
+
// Only after a chain of overrides; otherwise the two are one account.
|
|
2014
|
+
...failed !== source ? { failedAccount: failed } : {},
|
|
2015
|
+
...input.reason ? { reason: input.reason } : {},
|
|
2012
2016
|
candidates: [...candidates],
|
|
2013
2017
|
resetsAt: input.resetsAt,
|
|
2014
2018
|
question
|
|
@@ -2024,7 +2028,7 @@ function createAccountFailoverQuestionCall(sessionKey2, input, toolCallId = `${A
|
|
|
2024
2028
|
options: [
|
|
2025
2029
|
...candidates.map((candidate) => ({
|
|
2026
2030
|
label: candidate,
|
|
2027
|
-
description: input.carryTranscript === false ? `Run on "${candidate}" until ${until}. The conversation is replayed as a fresh Claude session, and any MCP server configured only in "${
|
|
2031
|
+
description: input.carryTranscript === false ? `Run on "${candidate}" until ${until}. The conversation is replayed as a fresh Claude session, and any MCP server configured only in "${failed}"'s Claude profile will be missing.` : `Run on "${candidate}" until ${until}. The conversation continues where it stopped if its Claude transcript can be carried across, and is replayed as a fresh Claude session if it cannot; either way, any MCP server configured only in "${failed}"'s Claude profile will be missing.`
|
|
2028
2032
|
})),
|
|
2029
2033
|
{
|
|
2030
2034
|
label: STOP_ANSWER,
|
|
@@ -2068,6 +2072,8 @@ function classify(pending2, part) {
|
|
|
2068
2072
|
kind: "switch",
|
|
2069
2073
|
target,
|
|
2070
2074
|
sourceAccount: pending2.sourceAccount,
|
|
2075
|
+
...pending2.failedAccount ? { failedAccount: pending2.failedAccount } : {},
|
|
2076
|
+
...pending2.reason ? { reason: pending2.reason } : {},
|
|
2071
2077
|
resetsAt: pending2.resetsAt
|
|
2072
2078
|
};
|
|
2073
2079
|
}
|
|
@@ -2095,6 +2101,7 @@ function consumeAccountFailoverAnswer(sessionKey2, prompt, fallback) {
|
|
|
2095
2101
|
return classify(
|
|
2096
2102
|
{
|
|
2097
2103
|
sourceAccount: normalizeAccountName(fallback.sourceAccount || DEFAULT_ACCOUNT),
|
|
2104
|
+
...fallback.failedAccount ? { failedAccount: normalizeAccountName(fallback.failedAccount) } : {},
|
|
2098
2105
|
candidates: fallback.candidates.map((c) => normalizeAccountName(c))
|
|
2099
2106
|
},
|
|
2100
2107
|
orphan
|
|
@@ -2105,8 +2112,9 @@ function consumeAccountFailoverAnswer(sessionKey2, prompt, fallback) {
|
|
|
2105
2112
|
}
|
|
2106
2113
|
function formatFailoverNote(input) {
|
|
2107
2114
|
const resets = describeReset(input.resetsAt);
|
|
2115
|
+
const arrival = input.carried ? "The conversation continues where it stopped." : "The conversation is replayed into a fresh Claude session there.";
|
|
2108
2116
|
return `
|
|
2109
|
-
${FAILOVER_MARKER} "${input.sourceAccount}" is out of usage, so this conversation continues on "${input.target}" ${resets ? `until ${resets}` : "until opencode restarts"}.
|
|
2117
|
+
${FAILOVER_MARKER} "${input.sourceAccount}" ${input.reason ?? "is out of usage"}, so this conversation continues on "${input.target}" ${resets ? `until ${resets}` : "until opencode restarts"}. ${arrival}
|
|
2110
2118
|
`;
|
|
2111
2119
|
}
|
|
2112
2120
|
function formatAutoFailoverNote(input) {
|
|
@@ -2614,9 +2622,12 @@ function withoutAccountMarker(modelId) {
|
|
|
2614
2622
|
}
|
|
2615
2623
|
function qualifyModelName(wanted, referenceModelId) {
|
|
2616
2624
|
const base = withoutAccountMarker(wanted.trim());
|
|
2617
|
-
if (!
|
|
2625
|
+
if (!isRegisteredModelName(base)) return null;
|
|
2618
2626
|
return `${base}${accountMarker(referenceModelId)}`;
|
|
2619
2627
|
}
|
|
2628
|
+
function isRegisteredModelName(name) {
|
|
2629
|
+
return name.length > 0 && Object.hasOwn(defaultModels, name);
|
|
2630
|
+
}
|
|
2620
2631
|
function resolveAgentModel(agent, modelId, overrides) {
|
|
2621
2632
|
if (!agent) return modelId;
|
|
2622
2633
|
const record = (overrides?.records ?? registry)[agent];
|
|
@@ -3797,13 +3808,27 @@ import { createHash } from "crypto";
|
|
|
3797
3808
|
var fingerprints = /* @__PURE__ */ new Map();
|
|
3798
3809
|
var fingerprintSeq = 0;
|
|
3799
3810
|
var MAX_FORK_FINGERPRINTS = 64;
|
|
3800
|
-
|
|
3811
|
+
var DIGEST_VERSION = 2;
|
|
3812
|
+
var DCP_MESSAGE_ID_TAG = /<dcp-message-id(?:\s[^>]*)?>[^<]*<\/dcp-message-id>/g;
|
|
3813
|
+
var DCP_COMPACT_REF_LINE = /^@(?:\d+|b\d+|blocked)@(?: \[[^\]\n]*\])?$/;
|
|
3814
|
+
function withoutDcpRefs(text) {
|
|
3815
|
+
let out = text.includes("<dcp-message-id") ? text.replace(DCP_MESSAGE_ID_TAG, "") : text;
|
|
3816
|
+
for (; ; ) {
|
|
3817
|
+
const trimmed = out.trimEnd();
|
|
3818
|
+
if (!trimmed.endsWith("@") && !trimmed.endsWith("]")) return out;
|
|
3819
|
+
const lineStart = trimmed.lastIndexOf("\n") + 1;
|
|
3820
|
+
if (!DCP_COMPACT_REF_LINE.test(trimmed.slice(lineStart))) return out;
|
|
3821
|
+
out = trimmed.slice(0, lineStart);
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
function hashPart(hash, part, version) {
|
|
3801
3825
|
const p = part;
|
|
3802
3826
|
const type = typeof p?.type === "string" ? p.type : "unknown";
|
|
3827
|
+
const content = (text) => version >= 2 ? withoutDcpRefs(text) : text;
|
|
3803
3828
|
switch (type) {
|
|
3804
3829
|
case "text":
|
|
3805
3830
|
hash.update("text\0");
|
|
3806
|
-
hash.update(String(p?.text ?? ""));
|
|
3831
|
+
hash.update(content(String(p?.text ?? "")));
|
|
3807
3832
|
break;
|
|
3808
3833
|
case "tool-call":
|
|
3809
3834
|
hash.update("call\0");
|
|
@@ -3815,7 +3840,7 @@ function hashPart(hash, part) {
|
|
|
3815
3840
|
hash.update("result\0");
|
|
3816
3841
|
hash.update(String(p?.toolName ?? ""));
|
|
3817
3842
|
hash.update("\0");
|
|
3818
|
-
hash.update(stableJson(p?.output));
|
|
3843
|
+
hash.update(stableJson(p?.output, version >= 2 ? withoutDcpRefs : void 0));
|
|
3819
3844
|
break;
|
|
3820
3845
|
case "file":
|
|
3821
3846
|
case "image": {
|
|
@@ -3837,9 +3862,10 @@ function hashPart(hash, part) {
|
|
|
3837
3862
|
}
|
|
3838
3863
|
hash.update("");
|
|
3839
3864
|
}
|
|
3840
|
-
function stableJson(value) {
|
|
3865
|
+
function stableJson(value, text) {
|
|
3841
3866
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
3842
3867
|
const walk = (node) => {
|
|
3868
|
+
if (typeof node === "string") return text ? text(node) : node;
|
|
3843
3869
|
if (node === null || typeof node !== "object") return node;
|
|
3844
3870
|
if (seen.has(node)) return "[circular]";
|
|
3845
3871
|
seen.add(node);
|
|
@@ -3856,16 +3882,16 @@ function stableJson(value) {
|
|
|
3856
3882
|
return "[unserializable]";
|
|
3857
3883
|
}
|
|
3858
3884
|
}
|
|
3859
|
-
function messageDigest(msg) {
|
|
3885
|
+
function messageDigest(msg, version) {
|
|
3860
3886
|
const hash = createHash("sha256");
|
|
3861
3887
|
hash.update(msg.role);
|
|
3862
3888
|
hash.update("\0");
|
|
3863
3889
|
if (typeof msg.content === "string") {
|
|
3864
3890
|
hash.update("text\0");
|
|
3865
|
-
hash.update(msg.content);
|
|
3891
|
+
hash.update(version >= 2 ? withoutDcpRefs(msg.content) : msg.content);
|
|
3866
3892
|
hash.update("");
|
|
3867
3893
|
} else if (Array.isArray(msg.content)) {
|
|
3868
|
-
for (const part of msg.content) hashPart(hash, part);
|
|
3894
|
+
for (const part of msg.content) hashPart(hash, part, version);
|
|
3869
3895
|
}
|
|
3870
3896
|
return hash.digest("hex").slice(0, 32);
|
|
3871
3897
|
}
|
|
@@ -3878,7 +3904,7 @@ function conversationDigests(prompt, opts) {
|
|
|
3878
3904
|
}
|
|
3879
3905
|
out.push({
|
|
3880
3906
|
role: msg.role,
|
|
3881
|
-
digest: opts?.assistantContent === false && msg.role === "assistant" ? ASSISTANT_TURN_DIGEST : messageDigest(msg)
|
|
3907
|
+
digest: opts?.assistantContent === false && msg.role === "assistant" ? ASSISTANT_TURN_DIGEST : messageDigest(msg, opts?.version ?? DIGEST_VERSION)
|
|
3882
3908
|
});
|
|
3883
3909
|
}
|
|
3884
3910
|
return out;
|
|
@@ -4248,6 +4274,13 @@ var SPAWN_MCP_TIMEOUT_MS = 12e4;
|
|
|
4248
4274
|
function spawnMcpTimeoutEnv(inherited = process.env) {
|
|
4249
4275
|
return inherited.MCP_TIMEOUT === void 0 ? { MCP_TIMEOUT: String(SPAWN_MCP_TIMEOUT_MS) } : {};
|
|
4250
4276
|
}
|
|
4277
|
+
var CLI_DEFAULT_MCP_TIMEOUT_MS = 3e4;
|
|
4278
|
+
function effectiveSpawnMcpTimeoutMs(inherited = process.env) {
|
|
4279
|
+
const value = inherited.MCP_TIMEOUT;
|
|
4280
|
+
if (value === void 0) return SPAWN_MCP_TIMEOUT_MS;
|
|
4281
|
+
const parsed = Number.parseInt(value, 10);
|
|
4282
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.min(parsed, 2147483647) : CLI_DEFAULT_MCP_TIMEOUT_MS;
|
|
4283
|
+
}
|
|
4251
4284
|
function detectCliVersion(cliPath) {
|
|
4252
4285
|
const cached2 = cache2.get(cliPath);
|
|
4253
4286
|
if (cached2) return cached2;
|
|
@@ -4793,6 +4826,13 @@ function taskSchemaOffersModel(def) {
|
|
|
4793
4826
|
const fields = schema?.properties;
|
|
4794
4827
|
return !!fields && typeof fields === "object" && "model" in fields;
|
|
4795
4828
|
}
|
|
4829
|
+
function completeTaskModels(def, input, complete) {
|
|
4830
|
+
if (!complete || !taskSchemaOffersModel(def)) return;
|
|
4831
|
+
const tasks = def.name === TASK_BATCH_TOOL_NAME ? taskBatchTasks(input) : [input];
|
|
4832
|
+
for (const task of tasks) {
|
|
4833
|
+
if (task.model !== void 0) task.model = complete(task.model);
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4796
4836
|
function taskModelInputError(def, input, guard) {
|
|
4797
4837
|
const tasks = def.name === TASK_BATCH_TOOL_NAME ? taskBatchTasks(input) : [input];
|
|
4798
4838
|
const offered = taskSchemaOffersModel(def);
|
|
@@ -5121,6 +5161,7 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
5121
5161
|
}
|
|
5122
5162
|
}
|
|
5123
5163
|
if (toolName === "task" || toolName === TASK_BATCH_TOOL_NAME) {
|
|
5164
|
+
completeTaskModels(tool, input, options.completeTaskModel);
|
|
5124
5165
|
const problem = taskModelInputError(tool, input, options.taskModelGuard);
|
|
5125
5166
|
if (problem) {
|
|
5126
5167
|
writeToolCallResult(res, requestId, { kind: "error", message: problem });
|
|
@@ -5894,6 +5935,21 @@ function parseTaskModelReference(value) {
|
|
|
5894
5935
|
const [, providerID, modelID, variant] = match;
|
|
5895
5936
|
return variant ? { providerID, modelID, variant } : { providerID, modelID };
|
|
5896
5937
|
}
|
|
5938
|
+
function completeTaskModelReference(value, own) {
|
|
5939
|
+
if (typeof value !== "string") return value;
|
|
5940
|
+
const trimmed = value.trim();
|
|
5941
|
+
if (!trimmed || trimmed.includes("/")) return value;
|
|
5942
|
+
const hash = trimmed.indexOf("#");
|
|
5943
|
+
const id = hash === -1 ? trimmed : trimmed.slice(0, hash);
|
|
5944
|
+
const variant = hash === -1 ? void 0 : trimmed.slice(hash + 1);
|
|
5945
|
+
if (variant !== void 0 && (!variant || variant.includes("#"))) return value;
|
|
5946
|
+
const at = id.indexOf("@");
|
|
5947
|
+
const base = at === -1 ? id : id.slice(0, at);
|
|
5948
|
+
if (at !== -1 && id.slice(at + 1) !== own.accountMarker) return value;
|
|
5949
|
+
if (!own.isRegistered(base)) return value;
|
|
5950
|
+
const modelID = own.accountMarker ? `${base}@${own.accountMarker}` : base;
|
|
5951
|
+
return `${own.providerID}/${modelID}${variant ? `#${variant}` : ""}`;
|
|
5952
|
+
}
|
|
5897
5953
|
function taskModelAccountRefusal(input) {
|
|
5898
5954
|
if (!input.groups || input.crossGroup) return null;
|
|
5899
5955
|
const reference = parseTaskModelReference(input.model);
|
|
@@ -7192,7 +7248,7 @@ function isDigest(value) {
|
|
|
7192
7248
|
}
|
|
7193
7249
|
function isRecord2(value) {
|
|
7194
7250
|
const record = value;
|
|
7195
|
-
return !!record && typeof record.claudeSessionId === "string" && typeof record.cliPath === "string" && typeof record.updatedAt === "number" && Array.isArray(record.chain) && record.chain.every(isDigest) && (record.shape === void 0 || Array.isArray(record.shape) && record.shape.every(isDigest)) && (record.configDir === void 0 || typeof record.configDir === "string") && (record.account === void 0 || typeof record.account === "string");
|
|
7251
|
+
return !!record && typeof record.claudeSessionId === "string" && typeof record.cliPath === "string" && typeof record.updatedAt === "number" && Array.isArray(record.chain) && record.chain.every(isDigest) && (record.shape === void 0 || Array.isArray(record.shape) && record.shape.every(isDigest)) && (record.configDir === void 0 || typeof record.configDir === "string") && (record.account === void 0 || typeof record.account === "string") && (record.version === void 0 || typeof record.version === "number");
|
|
7196
7252
|
}
|
|
7197
7253
|
function load2() {
|
|
7198
7254
|
if (records) return records;
|
|
@@ -7245,7 +7301,7 @@ function recordResumePoint(sessionKey2, claudeSessionId, prompt, cliPath, where)
|
|
|
7245
7301
|
const configDir = where?.configDir;
|
|
7246
7302
|
const account = where?.account;
|
|
7247
7303
|
const current = load2().get(sessionKey2);
|
|
7248
|
-
if (current && current.claudeSessionId === claudeSessionId && current.cliPath === cliPath && current.shape !== void 0 && current.configDir === configDir && current.account === account && current.chain.length === chain.length && current.chain.every((entry, i) => entry.digest === chain[i].digest)) {
|
|
7304
|
+
if (current && current.claudeSessionId === claudeSessionId && current.cliPath === cliPath && current.shape !== void 0 && current.configDir === configDir && current.account === account && current.version === DIGEST_VERSION && current.chain.length === chain.length && current.chain.every((entry, i) => entry.digest === chain[i].digest)) {
|
|
7249
7305
|
return;
|
|
7250
7306
|
}
|
|
7251
7307
|
save2((merged) => {
|
|
@@ -7257,6 +7313,7 @@ function recordResumePoint(sessionKey2, claudeSessionId, prompt, cliPath, where)
|
|
|
7257
7313
|
shape: shape2,
|
|
7258
7314
|
...configDir === void 0 ? {} : { configDir },
|
|
7259
7315
|
...account === void 0 ? {} : { account },
|
|
7316
|
+
version: DIGEST_VERSION,
|
|
7260
7317
|
updatedAt: Date.now()
|
|
7261
7318
|
});
|
|
7262
7319
|
});
|
|
@@ -7291,7 +7348,7 @@ function findResumePoint(opts) {
|
|
|
7291
7348
|
return void 0;
|
|
7292
7349
|
};
|
|
7293
7350
|
if (record.cliPath !== opts.cliPath) return refuse("another-claude-binary");
|
|
7294
|
-
if (!continuesRecordedConversation(record.chain, opts.prompt)) {
|
|
7351
|
+
if (!continuesRecordedConversation(record.chain, opts.prompt, { version: record.version ?? 1 })) {
|
|
7295
7352
|
return refuse("conversation-changed");
|
|
7296
7353
|
}
|
|
7297
7354
|
if (!existsSync2(opts.transcriptPath(record.claudeSessionId))) {
|
|
@@ -7329,7 +7386,8 @@ function findSiblingResumePoint(opts) {
|
|
|
7329
7386
|
continue;
|
|
7330
7387
|
}
|
|
7331
7388
|
if (!record.shape || !continuesRecordedConversation(record.shape, opts.prompt, {
|
|
7332
|
-
assistantContent: false
|
|
7389
|
+
assistantContent: false,
|
|
7390
|
+
version: record.version ?? 1
|
|
7333
7391
|
})) {
|
|
7334
7392
|
refusal = "sibling-conversation-changed";
|
|
7335
7393
|
continue;
|
|
@@ -7859,6 +7917,18 @@ function isTerminalResultLine(line) {
|
|
|
7859
7917
|
return false;
|
|
7860
7918
|
}
|
|
7861
7919
|
}
|
|
7920
|
+
function isSystemInitLine(line) {
|
|
7921
|
+
if (!line.includes('"init"')) return false;
|
|
7922
|
+
try {
|
|
7923
|
+
const parsed = JSON.parse(line);
|
|
7924
|
+
return parsed.type === "system" && parsed.subtype === "init";
|
|
7925
|
+
} catch {
|
|
7926
|
+
return false;
|
|
7927
|
+
}
|
|
7928
|
+
}
|
|
7929
|
+
function noteInitLine(ap, line) {
|
|
7930
|
+
if (!ap.initSeen && isSystemInitLine(line)) ap.initSeen = true;
|
|
7931
|
+
}
|
|
7862
7932
|
function settleTurn(ap) {
|
|
7863
7933
|
ap.turnInFlight = false;
|
|
7864
7934
|
const waiters = ap.turnIdleWaiters ?? [];
|
|
@@ -8220,6 +8290,7 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
|
|
|
8220
8290
|
rl.on("line", (line) => {
|
|
8221
8291
|
if (dispatchSideQuestionResponse(ap, line)) return;
|
|
8222
8292
|
noteTurnLine(ap, line);
|
|
8293
|
+
noteInitLine(ap, line);
|
|
8223
8294
|
if (lineEmitter.listenerCount("line") === 0) {
|
|
8224
8295
|
bufferUnattendedLine(ap, line);
|
|
8225
8296
|
return;
|
|
@@ -8834,9 +8905,10 @@ function resolveBudgetWarnAt(value) {
|
|
|
8834
8905
|
}
|
|
8835
8906
|
return [...new Set(value)].sort((a, b) => a - b);
|
|
8836
8907
|
}
|
|
8837
|
-
function
|
|
8838
|
-
if (!Number.isFinite(totalCostUsd) || totalCostUsd <= 0) return 0;
|
|
8839
|
-
if (!cumulative
|
|
8908
|
+
function takeResultCost(claudeSessionId, totalCostUsd, cumulative) {
|
|
8909
|
+
if (!Number.isFinite(totalCostUsd) || totalCostUsd <= 0) return { deltaUsd: 0, noBaseline: false };
|
|
8910
|
+
if (!cumulative) return { deltaUsd: totalCostUsd, noBaseline: false };
|
|
8911
|
+
if (!claudeSessionId) return { deltaUsd: totalCostUsd, noBaseline: true };
|
|
8840
8912
|
const previous = lastTotals.get(claudeSessionId);
|
|
8841
8913
|
lastTotals.delete(claudeSessionId);
|
|
8842
8914
|
lastTotals.set(claudeSessionId, totalCostUsd);
|
|
@@ -8845,8 +8917,9 @@ function costDelta(claudeSessionId, totalCostUsd, cumulative) {
|
|
|
8845
8917
|
if (oldest.done) break;
|
|
8846
8918
|
lastTotals.delete(oldest.value);
|
|
8847
8919
|
}
|
|
8848
|
-
if (previous === void 0
|
|
8849
|
-
return totalCostUsd
|
|
8920
|
+
if (previous === void 0) return { deltaUsd: totalCostUsd, noBaseline: true };
|
|
8921
|
+
if (totalCostUsd < previous) return { deltaUsd: totalCostUsd, noBaseline: false };
|
|
8922
|
+
return { deltaUsd: totalCostUsd - previous, noBaseline: false };
|
|
8850
8923
|
}
|
|
8851
8924
|
function recordSessionCost(session, deltaUsd) {
|
|
8852
8925
|
if (!(deltaUsd > 0)) return;
|
|
@@ -8905,8 +8978,10 @@ function formatBudgetStopNote(spentUsd, capUsd) {
|
|
|
8905
8978
|
return `${BUDGET_MARKER} this conversation has spent ${formatUsd(spentUsd)} against its ${formatUsd(capUsd)} \`maxBudgetUsd\`, so this turn was not started and nothing was sent to Claude. Raise \`maxBudgetUsd\` in the provider options and relaunch opencode (plugin options are read once at startup), or start a new session.`;
|
|
8906
8979
|
}
|
|
8907
8980
|
function recordResultCost(budget, claudeSessionId, totalCostUsd) {
|
|
8908
|
-
if (typeof totalCostUsd !== "number") return;
|
|
8909
|
-
|
|
8981
|
+
if (typeof totalCostUsd !== "number") return void 0;
|
|
8982
|
+
const cost = takeResultCost(claudeSessionId, totalCostUsd, budget.cumulative);
|
|
8983
|
+
recordSessionCost(budget.session, cost.deltaUsd);
|
|
8984
|
+
return cost;
|
|
8910
8985
|
}
|
|
8911
8986
|
function takeBudgetNote(budget) {
|
|
8912
8987
|
if (!budget.notes) return null;
|
|
@@ -9122,6 +9197,7 @@ var BUNDLE_DATA_ALLOWLIST = {
|
|
|
9122
9197
|
// booleans: every one of these is a decision the plugin made
|
|
9123
9198
|
applied: "flag",
|
|
9124
9199
|
attached: "flag",
|
|
9200
|
+
beforeInit: "flag",
|
|
9125
9201
|
bridgeSkipNativeSkills: "flag",
|
|
9126
9202
|
channelClosed: "flag",
|
|
9127
9203
|
compaction: "flag",
|
|
@@ -9223,9 +9299,11 @@ var BUNDLE_DATA_ALLOWLIST = {
|
|
|
9223
9299
|
durationMs: "duration",
|
|
9224
9300
|
elapsedMs: "duration",
|
|
9225
9301
|
remainingMs: "duration",
|
|
9302
|
+
startWatchdogMs: "duration",
|
|
9226
9303
|
timeoutMs: "duration",
|
|
9227
9304
|
waitedMs: "duration",
|
|
9228
9305
|
costUsd: "money",
|
|
9306
|
+
turnCostUsd: "money",
|
|
9229
9307
|
// enums: a fixed vocabulary this package or the CLI defines
|
|
9230
9308
|
apiKeySource: "enum",
|
|
9231
9309
|
delivery: "enum",
|
|
@@ -11670,6 +11748,11 @@ function extractTurnStats(msg) {
|
|
|
11670
11748
|
if (Array.isArray(msg.permission_denials)) stats.permissionDenials = msg.permission_denials;
|
|
11671
11749
|
return stats;
|
|
11672
11750
|
}
|
|
11751
|
+
function footerCostUsd(cost, resumedSession) {
|
|
11752
|
+
if (!cost) return void 0;
|
|
11753
|
+
if (cost.noBaseline && resumedSession) return void 0;
|
|
11754
|
+
return cost.deltaUsd;
|
|
11755
|
+
}
|
|
11673
11756
|
function formatCost(costUsd) {
|
|
11674
11757
|
if (!Number.isFinite(costUsd) || costUsd < 0) return "$0.00";
|
|
11675
11758
|
return costUsd >= 1 ? `$${costUsd.toFixed(2)}` : `$${costUsd.toFixed(4)}`;
|
|
@@ -15226,9 +15309,24 @@ async function ensureProxyServer(config, tools, sessionKeyForCalls, interceptCom
|
|
|
15226
15309
|
groups,
|
|
15227
15310
|
crossGroup: config.subagentDispatchCrossGroup === true
|
|
15228
15311
|
}) : void 0;
|
|
15312
|
+
const ownModelMarker = accountModelSuffix(config.account ?? DEFAULT_ACCOUNT);
|
|
15313
|
+
const completeTaskModel = (model) => {
|
|
15314
|
+
const completed = completeTaskModelReference(model, {
|
|
15315
|
+
providerID: config.provider,
|
|
15316
|
+
accountMarker: ownModelMarker,
|
|
15317
|
+
isRegistered: isRegisteredModelName
|
|
15318
|
+
});
|
|
15319
|
+
if (completed !== model) {
|
|
15320
|
+
log.info("bare task model completed to this conversation's own provider", {
|
|
15321
|
+
model: completed
|
|
15322
|
+
});
|
|
15323
|
+
}
|
|
15324
|
+
return completed;
|
|
15325
|
+
};
|
|
15229
15326
|
const srv = await createProxyMcpServer(tools, timeoutOverrides, interceptors, {
|
|
15230
15327
|
admit,
|
|
15231
|
-
taskModelGuard
|
|
15328
|
+
taskModelGuard,
|
|
15329
|
+
completeTaskModel
|
|
15232
15330
|
});
|
|
15233
15331
|
srv.calls.on("call", (call) => {
|
|
15234
15332
|
if (call.toolName === "task") noteExplicitTaskModels(callerSessionId, [call.input]);
|
|
@@ -15518,6 +15616,8 @@ function createTurnState(init) {
|
|
|
15518
15616
|
asideSinkUnregister: null,
|
|
15519
15617
|
resultFallbackTimer: null,
|
|
15520
15618
|
startWatchdog: null,
|
|
15619
|
+
startWatchdogArmedMs: 0,
|
|
15620
|
+
startWatchdogBeforeInit: false,
|
|
15521
15621
|
respawnAttempted: false,
|
|
15522
15622
|
hasReceivedContent: false,
|
|
15523
15623
|
hasReceivedProgress: false,
|
|
@@ -15893,7 +15993,11 @@ function onStartWatchdogFire(state) {
|
|
|
15893
15993
|
if (state.respawnAttempted) {
|
|
15894
15994
|
log.error(
|
|
15895
15995
|
"claude process still silent after respawn; ending turn",
|
|
15896
|
-
{
|
|
15996
|
+
{
|
|
15997
|
+
sessionKey: state.sessionKey,
|
|
15998
|
+
startWatchdogMs: state.startWatchdogArmedMs,
|
|
15999
|
+
beforeInit: state.startWatchdogBeforeInit
|
|
16000
|
+
}
|
|
15897
16001
|
);
|
|
15898
16002
|
deleteActiveProcess(state.sessionKey);
|
|
15899
16003
|
deleteClaudeSessionId(state.sessionKey);
|
|
@@ -15914,7 +16018,11 @@ function onStartWatchdogFire(state) {
|
|
|
15914
16018
|
state.respawnAttempted = true;
|
|
15915
16019
|
log.warn(
|
|
15916
16020
|
"no stdout after envelope write; respawning claude process to resume conversation",
|
|
15917
|
-
{
|
|
16021
|
+
{
|
|
16022
|
+
sessionKey: state.sessionKey,
|
|
16023
|
+
startWatchdogMs: state.startWatchdogArmedMs,
|
|
16024
|
+
beforeInit: state.startWatchdogBeforeInit
|
|
16025
|
+
}
|
|
15918
16026
|
);
|
|
15919
16027
|
state.lineEmitter.off("line", state.lineHandler);
|
|
15920
16028
|
state.lineEmitter.off("close", state.closeHandler);
|
|
@@ -15966,14 +16074,29 @@ function onStartWatchdogFire(state) {
|
|
|
15966
16074
|
}
|
|
15967
16075
|
armStartWatchdog(state);
|
|
15968
16076
|
}
|
|
16077
|
+
var START_WATCHDOG_INIT_MARGIN_MS = 3e4;
|
|
16078
|
+
function startWatchdogDelayMs(configuredMs, beforeInit, mcpTimeoutMs = effectiveSpawnMcpTimeoutMs()) {
|
|
16079
|
+
return beforeInit ? Math.max(configuredMs, mcpTimeoutMs + START_WATCHDOG_INIT_MARGIN_MS) : configuredMs;
|
|
16080
|
+
}
|
|
15969
16081
|
function armStartWatchdog(state) {
|
|
15970
16082
|
clearStartWatchdog(state);
|
|
15971
16083
|
if (state.controllerClosed) return;
|
|
16084
|
+
const ap = state.activeProcess;
|
|
16085
|
+
state.startWatchdogBeforeInit = !ap?.interactiveControl && ap?.initSeen !== true;
|
|
16086
|
+
state.startWatchdogArmedMs = startWatchdogDelayMs(
|
|
16087
|
+
state.startWatchdogMs,
|
|
16088
|
+
state.startWatchdogBeforeInit
|
|
16089
|
+
);
|
|
15972
16090
|
state.startWatchdog = setTimeout(
|
|
15973
16091
|
() => onStartWatchdogFire(state),
|
|
15974
|
-
state.
|
|
16092
|
+
state.startWatchdogArmedMs
|
|
15975
16093
|
);
|
|
15976
16094
|
}
|
|
16095
|
+
function noteStartWatchdogInit(state) {
|
|
16096
|
+
if (!state.startWatchdog || !state.startWatchdogBeforeInit) return;
|
|
16097
|
+
if (state.activeProcess?.initSeen !== true) return;
|
|
16098
|
+
armStartWatchdog(state);
|
|
16099
|
+
}
|
|
15977
16100
|
function deliverPendingCompletions(state, force = false) {
|
|
15978
16101
|
const pending2 = state.activeProcess?.pendingProxyCompletions;
|
|
15979
16102
|
const entries = [...pending2?.values() ?? []].filter(
|
|
@@ -16329,6 +16452,7 @@ function createLineHandler(state, ctx) {
|
|
|
16329
16452
|
subtype: msg.subtype
|
|
16330
16453
|
});
|
|
16331
16454
|
if (msg.type === "system" && msg.subtype === "init") {
|
|
16455
|
+
noteStartWatchdogInit(state);
|
|
16332
16456
|
if (msg.session_id) {
|
|
16333
16457
|
setClaudeSessionId(state.sessionKey, msg.session_id);
|
|
16334
16458
|
log.info("session initialized", {
|
|
@@ -16927,7 +17051,7 @@ ${plan}
|
|
|
16927
17051
|
if (msg.session_id) {
|
|
16928
17052
|
setClaudeSessionId(state.sessionKey, msg.session_id);
|
|
16929
17053
|
}
|
|
16930
|
-
|
|
17054
|
+
const resultCost = ctx.budget ? recordResultCost(ctx.budget, msg.session_id, msg.total_cost_usd) : ctx.config.turnStats && typeof msg.total_cost_usd === "number" ? takeResultCost(msg.session_id, msg.total_cost_usd, !ctx.interactive) : void 0;
|
|
16931
17055
|
if (deliverPendingCompletions(state)) {
|
|
16932
17056
|
return;
|
|
16933
17057
|
}
|
|
@@ -17020,10 +17144,17 @@ ${plan}
|
|
|
17020
17144
|
numTurns: msg.num_turns,
|
|
17021
17145
|
isError: msg.is_error,
|
|
17022
17146
|
subtype: msg.subtype,
|
|
17023
|
-
...turnStatsLogPayload(turnStats)
|
|
17147
|
+
...turnStatsLogPayload(turnStats),
|
|
17148
|
+
turnCostUsd: resultCost?.deltaUsd
|
|
17024
17149
|
});
|
|
17025
17150
|
if (ctx.config.turnStats && !ctx.compactionMode && !msg.is_error && !failure) {
|
|
17026
|
-
const footer = formatTurnStatsBlock(
|
|
17151
|
+
const footer = formatTurnStatsBlock({
|
|
17152
|
+
...turnStats,
|
|
17153
|
+
costUsd: footerCostUsd(
|
|
17154
|
+
resultCost,
|
|
17155
|
+
state.activeProcess?.cliArgs?.includes("--resume") === true
|
|
17156
|
+
)
|
|
17157
|
+
});
|
|
17027
17158
|
if (footer) {
|
|
17028
17159
|
state.controller.enqueue({
|
|
17029
17160
|
type: "text-delta",
|
|
@@ -17098,6 +17229,26 @@ async function isProxyCallStillServed(callId) {
|
|
|
17098
17229
|
setProxyDeadlineGuard(({ callId }) => isProxyCallStillServed(callId));
|
|
17099
17230
|
var FORWARD_SETTLE_MS = 250;
|
|
17100
17231
|
var CHILD_EXIT_STATUS_GRACE_MS = 250;
|
|
17232
|
+
function stopOnCancel(stream, cancelled2) {
|
|
17233
|
+
const reader = stream.getReader();
|
|
17234
|
+
return new ReadableStream({
|
|
17235
|
+
async pull(controller) {
|
|
17236
|
+
try {
|
|
17237
|
+
const { done, value } = await reader.read();
|
|
17238
|
+
if (done) controller.close();
|
|
17239
|
+
else controller.enqueue(value);
|
|
17240
|
+
} catch (error) {
|
|
17241
|
+
try {
|
|
17242
|
+
controller.error(error);
|
|
17243
|
+
} catch {
|
|
17244
|
+
}
|
|
17245
|
+
}
|
|
17246
|
+
},
|
|
17247
|
+
cancel(reason) {
|
|
17248
|
+
cancelled2.abort(reason);
|
|
17249
|
+
}
|
|
17250
|
+
});
|
|
17251
|
+
}
|
|
17101
17252
|
var ClaudeCodeLanguageModel = class {
|
|
17102
17253
|
specificationVersion = "v3";
|
|
17103
17254
|
modelId;
|
|
@@ -17585,32 +17736,35 @@ var ClaudeCodeLanguageModel = class {
|
|
|
17585
17736
|
* switch, and outside `runModelChain` on purpose: an account switch starts
|
|
17586
17737
|
* that account's own model chain from the top.
|
|
17587
17738
|
*
|
|
17588
|
-
* Anything but `"auto"` is `runModelChain`
|
|
17589
|
-
* `"
|
|
17590
|
-
*
|
|
17591
|
-
*
|
|
17592
|
-
*
|
|
17593
|
-
*
|
|
17594
|
-
* attempt did not switch.
|
|
17739
|
+
* Anything but `"auto"` is `runModelChain` behind `stopOnCancel`. With
|
|
17740
|
+
* `"auto"`, the attempt that failed decides the switch itself
|
|
17741
|
+
* (`completeResult` sets `run.switchTo` and closes its stream without a
|
|
17742
|
+
* `finish`), and the next attempt's prologue makes the switch the form's pick
|
|
17743
|
+
* would have made. This only splices the attempts into one caller turn: one
|
|
17744
|
+
* `stream-start`, and the `finish` of whichever attempt did not switch.
|
|
17595
17745
|
*
|
|
17596
17746
|
* The bound is `run.tried`, as it is `tried` for the model chain: every
|
|
17597
17747
|
* account spawned or switched to goes in, and none in it is picked again.
|
|
17598
17748
|
*
|
|
17599
|
-
* Cancelling the returned stream is a stop,
|
|
17600
|
-
* the
|
|
17601
|
-
*
|
|
17602
|
-
*
|
|
17603
|
-
* starts no further account
|
|
17749
|
+
* Cancelling the returned stream is a stop, in every mode: a caller that has
|
|
17750
|
+
* gone away must reach the turn it started, or the CLI runs it to the end
|
|
17751
|
+
* and bills it. Each attempt gets a signal that the caller's own
|
|
17752
|
+
* `abortSignal` and a `cancel()` both abort, so a cancel takes the turn's
|
|
17753
|
+
* ordinary abort path, and with `"auto"` the loop starts no further account
|
|
17754
|
+
* once either has happened. Until (h #g257) only `"auto"` did this.
|
|
17604
17755
|
*/
|
|
17605
17756
|
async runAccountFailover(options, mode) {
|
|
17606
|
-
if (this.config.accountFailover !== "auto") return this.runModelChain(options, mode);
|
|
17607
|
-
const self = this;
|
|
17608
|
-
const run = { tried: /* @__PURE__ */ new Set() };
|
|
17609
17757
|
const cancelled2 = new AbortController();
|
|
17610
17758
|
const attemptOptions = {
|
|
17611
17759
|
...options,
|
|
17612
17760
|
abortSignal: options.abortSignal ? AbortSignal.any([options.abortSignal, cancelled2.signal]) : cancelled2.signal
|
|
17613
17761
|
};
|
|
17762
|
+
if (this.config.accountFailover !== "auto") {
|
|
17763
|
+
const inner2 = await this.runModelChain(attemptOptions, mode);
|
|
17764
|
+
return { ...inner2, stream: stopOnCancel(inner2.stream, cancelled2) };
|
|
17765
|
+
}
|
|
17766
|
+
const self = this;
|
|
17767
|
+
const run = { tried: /* @__PURE__ */ new Set() };
|
|
17614
17768
|
const stopped = () => attemptOptions.abortSignal.aborted;
|
|
17615
17769
|
let inner = await this.runModelChain(attemptOptions, mode, run);
|
|
17616
17770
|
const request = inner.request;
|
|
@@ -17652,9 +17806,8 @@ var ClaudeCodeLanguageModel = class {
|
|
|
17652
17806
|
}
|
|
17653
17807
|
}
|
|
17654
17808
|
},
|
|
17655
|
-
|
|
17809
|
+
cancel(reason) {
|
|
17656
17810
|
cancelled2.abort(reason);
|
|
17657
|
-
await reader?.cancel(reason).catch(() => void 0);
|
|
17658
17811
|
}
|
|
17659
17812
|
});
|
|
17660
17813
|
return { stream, request };
|
|
@@ -18061,16 +18214,19 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18061
18214
|
resetsAt: autoSwitch.resetsAt
|
|
18062
18215
|
} : compactionMode || mode === "generate" ? null : consumeAccountFailoverAnswer(sk, options.prompt, {
|
|
18063
18216
|
sourceAccount,
|
|
18217
|
+
// What the form would offer now: an override still standing is
|
|
18218
|
+
// still the account that failed, and is not on offer (h #g257).
|
|
18219
|
+
failedAccount: failover.target ?? sourceAccount,
|
|
18064
18220
|
candidates: failoverCandidates(
|
|
18065
18221
|
this.config.failoverAccounts,
|
|
18066
18222
|
sourceAccount,
|
|
18067
18223
|
groups
|
|
18068
|
-
)
|
|
18224
|
+
).filter((account) => account !== (failover.target ?? sourceAccount))
|
|
18069
18225
|
});
|
|
18070
18226
|
if (failoverAnswer?.kind === "stop") {
|
|
18071
18227
|
log.warn("account failover declined; ending the turn", {
|
|
18072
18228
|
sessionKey: sk,
|
|
18073
|
-
account: sourceAccount,
|
|
18229
|
+
account: failover.target ?? sourceAccount,
|
|
18074
18230
|
reason: failoverAnswer.reason
|
|
18075
18231
|
});
|
|
18076
18232
|
const note = formatFailoverStopNote(failoverAnswer.reason);
|
|
@@ -18084,7 +18240,10 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18084
18240
|
controller.enqueue({
|
|
18085
18241
|
type: "error",
|
|
18086
18242
|
error: new Error(
|
|
18087
|
-
|
|
18243
|
+
// The account that failed: after a chain of overrides that is
|
|
18244
|
+
// not the provider's own (h #g257). A stop answer also follows
|
|
18245
|
+
// an expired login, so this does not say "out of usage".
|
|
18246
|
+
`Claude account "${failover.target ?? sourceAccount}" cannot serve this turn and no other account was picked.`
|
|
18088
18247
|
)
|
|
18089
18248
|
});
|
|
18090
18249
|
controller.enqueue({
|
|
@@ -18188,13 +18347,6 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18188
18347
|
}
|
|
18189
18348
|
cliPath = failover.cliPath;
|
|
18190
18349
|
asideTransportRef.cliPath = cliPath;
|
|
18191
|
-
if (!autoSwitch) {
|
|
18192
|
-
failoverNote = formatFailoverNote({
|
|
18193
|
-
sourceAccount: failoverAnswer.sourceAccount,
|
|
18194
|
-
target: failoverAnswer.target,
|
|
18195
|
-
resetsAt: failoverAnswer.resetsAt
|
|
18196
|
-
});
|
|
18197
|
-
}
|
|
18198
18350
|
}
|
|
18199
18351
|
const spawnConfigDir = this.config.accountInProcess ? this.skillBridgeSpawn(failover).configDir : void 0;
|
|
18200
18352
|
const processForAccount = getActiveProcess(sk);
|
|
@@ -18266,6 +18418,14 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18266
18418
|
reason: autoSwitch.reason,
|
|
18267
18419
|
carried: conversationOnTarget
|
|
18268
18420
|
});
|
|
18421
|
+
} else if (!autoSwitch && failoverAnswer?.kind === "switch") {
|
|
18422
|
+
failoverNote = formatFailoverNote({
|
|
18423
|
+
sourceAccount: failoverAnswer.failedAccount ?? failoverAnswer.sourceAccount,
|
|
18424
|
+
target: failoverAnswer.target,
|
|
18425
|
+
resetsAt: failoverAnswer.resetsAt,
|
|
18426
|
+
reason: failoverAnswer.reason,
|
|
18427
|
+
carried: conversationOnTarget
|
|
18428
|
+
});
|
|
18269
18429
|
}
|
|
18270
18430
|
let accountGroupNote = null;
|
|
18271
18431
|
if (!compactionMode && groups && accountProviders.size > 0 && includeHistoryContext && failoverAnswer?.kind !== "switch") {
|
|
@@ -18532,11 +18692,12 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18532
18692
|
"The read-only preset on the interactive transport needs Claude Code 2.1.263 or newer (--restricted and --permission-mode dontAsk). Update claude, or use the headless transport."
|
|
18533
18693
|
);
|
|
18534
18694
|
}
|
|
18695
|
+
const spawnedAccount = failover.target ?? sourceAccount;
|
|
18535
18696
|
const failoverAccounts = failoverCandidates(
|
|
18536
18697
|
this.config.failoverAccounts,
|
|
18537
18698
|
sourceAccount,
|
|
18538
18699
|
groups
|
|
18539
|
-
);
|
|
18700
|
+
).filter((account) => account !== spawnedAccount);
|
|
18540
18701
|
const failoverAskActive = failoverAccounts.length > 0 && this.config.accountFailover === "ask" && !compactionMode && // A `doGenerate` caller takes the override and reports the plain
|
|
18541
18702
|
// rate-limit error, never the form. See `TurnMode`.
|
|
18542
18703
|
mode !== "generate" && isAccountFailoverQuestionActive({
|
|
@@ -18549,7 +18710,6 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18549
18710
|
// the operator is usually not even looking at.
|
|
18550
18711
|
childSession: !!await fetchSessionParentId(affinity)
|
|
18551
18712
|
});
|
|
18552
|
-
const spawnedAccount = failover.target ?? sourceAccount;
|
|
18553
18713
|
accountRun?.tried.add(spawnedAccount);
|
|
18554
18714
|
let autoFailoverChoice;
|
|
18555
18715
|
const autoFailoverTarget = () => {
|
|
@@ -18558,7 +18718,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18558
18718
|
}
|
|
18559
18719
|
return autoFailoverChoice ?? void 0;
|
|
18560
18720
|
};
|
|
18561
|
-
const limitAccount =
|
|
18721
|
+
const limitAccount = spawnedAccount;
|
|
18562
18722
|
const usageLimitNoteActive = !compactionMode && mode !== "generate" && !failoverAskActive;
|
|
18563
18723
|
const usageWarnThresholds = resolveUsageWarnThresholds(this.config.usageWarnThresholds);
|
|
18564
18724
|
const usageSwitchAt = this.config.usageSwitchAt === null ? null : resolveUsageSwitchAt(this.config.usageSwitchAt);
|
|
@@ -19307,7 +19467,11 @@ var ClaudeCodeLanguageModel = class {
|
|
|
19307
19467
|
state.activeProcess?.pendingProxyCompletions?.clear();
|
|
19308
19468
|
if ((state.accountLimitHit || state.accountBlock) && failoverAskActive && msg.is_error === true) {
|
|
19309
19469
|
const call = createAccountFailoverQuestionCall(sk, {
|
|
19470
|
+
// The override a pick sets is keyed on the provider's own
|
|
19471
|
+
// account, the one every turn of this conversation resolves
|
|
19472
|
+
// from; the form names the account that actually failed.
|
|
19310
19473
|
sourceAccount,
|
|
19474
|
+
failedAccount: limitAccount,
|
|
19311
19475
|
candidates: failoverAccounts,
|
|
19312
19476
|
resetsAt: state.accountLimitHit?.resetsAt,
|
|
19313
19477
|
window: state.accountLimitHit?.window,
|
|
@@ -19315,7 +19479,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
19315
19479
|
carryTranscript: self.config.crossAccountResume !== false
|
|
19316
19480
|
});
|
|
19317
19481
|
log.warn(
|
|
19318
|
-
`Claude account "${
|
|
19482
|
+
`Claude account "${limitAccount}" ${state.accountLimitHit || !state.accountBlock ? "is out of usage" : `cannot serve (${state.accountBlock})`}; asking which account to continue on.`,
|
|
19319
19483
|
{
|
|
19320
19484
|
sessionKey: sk,
|
|
19321
19485
|
candidates: failoverAccounts,
|
|
@@ -19406,7 +19570,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
19406
19570
|
state.endTextBlock();
|
|
19407
19571
|
state.resultFailure ??= "usage_limit";
|
|
19408
19572
|
}
|
|
19409
|
-
if (
|
|
19573
|
+
if (state.accountLimitHit && msg.is_error === true) {
|
|
19410
19574
|
state.resultFailure ??= "usage_limit";
|
|
19411
19575
|
}
|
|
19412
19576
|
if (runAutoContinue(state, msg)) return;
|
|
@@ -19525,7 +19689,11 @@ var ClaudeCodeLanguageModel = class {
|
|
|
19525
19689
|
interactive: useInteractive,
|
|
19526
19690
|
sourceAccount: limitAccount,
|
|
19527
19691
|
usageAccount: spawnedAccount,
|
|
19528
|
-
|
|
19692
|
+
// The config dir of the account that ran, so an account-block note
|
|
19693
|
+
// names that account's login command. The provider's own when no
|
|
19694
|
+
// override moved the turn; until (h #g257) `"off"` and `"ask"`
|
|
19695
|
+
// named the provider's even after one had.
|
|
19696
|
+
accountConfigDir: self.skillBridgeSpawn(failover).configDir,
|
|
19529
19697
|
failoverAskActive,
|
|
19530
19698
|
autoFailoverTarget,
|
|
19531
19699
|
usageLimitNoteActive,
|