@khalilgharbaoui/opencode-claude-code-plugin 0.62.1 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +347 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +10 -3
package/dist/index.js
CHANGED
|
@@ -2045,6 +2045,10 @@ function clearAccountFailoverQuestions(sessionKey2) {
|
|
|
2045
2045
|
if (key.startsWith(prefix)) pendingQuestions2.delete(key);
|
|
2046
2046
|
}
|
|
2047
2047
|
}
|
|
2048
|
+
function hasAccountFailoverQuestions(sessionKey2) {
|
|
2049
|
+
const prefix = `${sessionKey2}${KEY_SEPARATOR2}`;
|
|
2050
|
+
return [...pendingQuestions2.keys()].some((key) => key.startsWith(prefix));
|
|
2051
|
+
}
|
|
2048
2052
|
function describeReset(resetsAt) {
|
|
2049
2053
|
return formatResetsAt(resetsAt);
|
|
2050
2054
|
}
|
|
@@ -3164,6 +3168,10 @@ function clearSubagentDispatchQuestions(sessionKey2) {
|
|
|
3164
3168
|
if (key.startsWith(prefix)) pending.delete(key);
|
|
3165
3169
|
}
|
|
3166
3170
|
}
|
|
3171
|
+
function hasPendingSubagentDispatch(sessionKey2) {
|
|
3172
|
+
const prefix = `${sessionKey2}${KEY_SEPARATOR3}`;
|
|
3173
|
+
return [...pending.keys()].some((key) => key.startsWith(prefix));
|
|
3174
|
+
}
|
|
3167
3175
|
function isSubagentDispatchActive(input) {
|
|
3168
3176
|
if (input.compactionMode) return false;
|
|
3169
3177
|
if (input.childSession) return false;
|
|
@@ -5833,6 +5841,9 @@ function isPendingProxyCallChannelClosed(call) {
|
|
|
5833
5841
|
function findPendingProxyCall(toolCallId) {
|
|
5834
5842
|
return pendingByCallId.get(toolCallId);
|
|
5835
5843
|
}
|
|
5844
|
+
function hasAnyPendingProxyCall() {
|
|
5845
|
+
return pendingByCallId.size > 0;
|
|
5846
|
+
}
|
|
5836
5847
|
function getPendingProxyCalls(sessionKey2) {
|
|
5837
5848
|
const s = callIdsBySession.get(sessionKey2);
|
|
5838
5849
|
if (!s || s.size === 0) return [];
|
|
@@ -6074,6 +6085,10 @@ function noteExplicitTaskModels(parentSessionId, tasks, now = Date.now()) {
|
|
|
6074
6085
|
}
|
|
6075
6086
|
return noted;
|
|
6076
6087
|
}
|
|
6088
|
+
function hasUnclaimedExplicitTaskModels(now = Date.now()) {
|
|
6089
|
+
pruneClaims2(now);
|
|
6090
|
+
return claims2.length > 0;
|
|
6091
|
+
}
|
|
6077
6092
|
function explicitTaskModelFor(sessionId) {
|
|
6078
6093
|
return sessionModels.get(sessionId);
|
|
6079
6094
|
}
|
|
@@ -7895,6 +7910,12 @@ function noteRelayMemberServing() {
|
|
|
7895
7910
|
ownServed = true;
|
|
7896
7911
|
join10();
|
|
7897
7912
|
}
|
|
7913
|
+
function relayMemberHasServed() {
|
|
7914
|
+
return ownServed;
|
|
7915
|
+
}
|
|
7916
|
+
function leaveSessionEventRelay() {
|
|
7917
|
+
leave();
|
|
7918
|
+
}
|
|
7898
7919
|
function openRelayFollower() {
|
|
7899
7920
|
const state = relayState();
|
|
7900
7921
|
join10();
|
|
@@ -8327,6 +8348,10 @@ function setClaudeSessionId(key, sessionId) {
|
|
|
8327
8348
|
capClaudeSessions();
|
|
8328
8349
|
}
|
|
8329
8350
|
function deleteClaudeSessionId(key) {
|
|
8351
|
+
forgetClaudeSessionInMemory(key);
|
|
8352
|
+
forgetResumePoint(key);
|
|
8353
|
+
}
|
|
8354
|
+
function forgetClaudeSessionInMemory(key) {
|
|
8330
8355
|
clearExitPlanModeQuestions(key);
|
|
8331
8356
|
clearAccountFailoverQuestions(key);
|
|
8332
8357
|
clearSubagentDispatchQuestions(key);
|
|
@@ -8335,7 +8360,52 @@ function deleteClaudeSessionId(key) {
|
|
|
8335
8360
|
claudeSessions.delete(key);
|
|
8336
8361
|
forgetForkFingerprint(key);
|
|
8337
8362
|
forgetServedConversation(key);
|
|
8338
|
-
|
|
8363
|
+
}
|
|
8364
|
+
function keyBelongsToSession(key, sessionID, ap) {
|
|
8365
|
+
return ap?.opencodeSessionID === sessionID || describeSessionKey(key).session === sessionID;
|
|
8366
|
+
}
|
|
8367
|
+
function keyHeldHere(key) {
|
|
8368
|
+
return claudeSessionIsWriting(key) || hasAccountFailoverQuestions(key) || hasPendingSubagentDispatch(key);
|
|
8369
|
+
}
|
|
8370
|
+
function sessionHeldHere(sessionID) {
|
|
8371
|
+
if (!sessionID || sessionID === "default") return false;
|
|
8372
|
+
for (const [key, ap] of activeProcesses) {
|
|
8373
|
+
if (keyBelongsToSession(key, sessionID, ap) && keyHeldHere(key)) return true;
|
|
8374
|
+
}
|
|
8375
|
+
for (const key of claudeSessions.keys()) {
|
|
8376
|
+
if (keyBelongsToSession(key, sessionID) && keyHeldHere(key)) return true;
|
|
8377
|
+
}
|
|
8378
|
+
return false;
|
|
8379
|
+
}
|
|
8380
|
+
function noWorkInFlightHere() {
|
|
8381
|
+
for (const ap of activeProcesses.values()) {
|
|
8382
|
+
if (isTurnInFlight(ap)) return false;
|
|
8383
|
+
}
|
|
8384
|
+
return !hasAnyPendingProxyCall() && !hasUnclaimedExplicitTaskModels();
|
|
8385
|
+
}
|
|
8386
|
+
function holdsNoClaudeState() {
|
|
8387
|
+
return activeProcesses.size === 0 && claudeSessions.size === 0 && !hasAnyPendingProxyCall();
|
|
8388
|
+
}
|
|
8389
|
+
async function releaseIdleForHandover(sessionID) {
|
|
8390
|
+
if (sessionID === "default") return [];
|
|
8391
|
+
const belongs = (key, ap) => sessionID === void 0 || keyBelongsToSession(key, sessionID, ap);
|
|
8392
|
+
const released = [];
|
|
8393
|
+
const exits = [];
|
|
8394
|
+
for (const [key, ap] of [...activeProcesses]) {
|
|
8395
|
+
if (!belongs(key, ap) || keyHeldHere(key)) continue;
|
|
8396
|
+
exits.push(deleteActiveProcessAndWait(key));
|
|
8397
|
+
released.push(key);
|
|
8398
|
+
}
|
|
8399
|
+
await Promise.all(exits);
|
|
8400
|
+
for (const key of [...claudeSessions.keys()]) {
|
|
8401
|
+
if (!belongs(key) || keyHeldHere(key)) continue;
|
|
8402
|
+
forgetClaudeSessionInMemory(key);
|
|
8403
|
+
if (!released.includes(key)) released.push(key);
|
|
8404
|
+
}
|
|
8405
|
+
if (released.length > 0) {
|
|
8406
|
+
log.info("handed claude sessions to another copy of the plugin", { sessionID, released });
|
|
8407
|
+
}
|
|
8408
|
+
return released;
|
|
8339
8409
|
}
|
|
8340
8410
|
function transferClaudeSession(fromKey, toKey) {
|
|
8341
8411
|
if (fromKey === toKey) return void 0;
|
|
@@ -9166,6 +9236,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
9166
9236
|
"agent maxTurns refused: not a positive whole number",
|
|
9167
9237
|
"agent model override refused: unknown model",
|
|
9168
9238
|
"agent prompt cache ttl refused: unknown value",
|
|
9239
|
+
"an older plugin build failed to release its claude sessions",
|
|
9169
9240
|
"an opencode read deny rule could not be mapped onto Claude's own Read; that path stays readable by the CLI's own file tools",
|
|
9170
9241
|
"auto-continuation stopped",
|
|
9171
9242
|
"auto-continuing incomplete claude result",
|
|
@@ -9312,6 +9383,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
9312
9383
|
"task_batch resolving with child results missing",
|
|
9313
9384
|
"the interactive transport has not been measured on this Claude Code version; it reads the TUI's screen and transcript, which a new release can change. If a turn hangs or a dialog is not handled, run /claude-code-doctor bundle and report it",
|
|
9314
9385
|
"the interactive turn did not take a forwarded user message",
|
|
9386
|
+
"the plugin build chosen for this turn could not take it; running it on this one",
|
|
9315
9387
|
"this opencode process is running an older plugin build than the one on disk",
|
|
9316
9388
|
"timed out pending proxy call",
|
|
9317
9389
|
"turn abort listener threw",
|
|
@@ -10017,7 +10089,7 @@ function emptyMapping(enabled) {
|
|
|
10017
10089
|
}
|
|
10018
10090
|
function mapReadRules(rules, options = {}) {
|
|
10019
10091
|
const deny = [];
|
|
10020
|
-
const
|
|
10092
|
+
const ask2 = [];
|
|
10021
10093
|
const unmapped = [];
|
|
10022
10094
|
const shadowedAllows = [];
|
|
10023
10095
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -10025,7 +10097,7 @@ function mapReadRules(rules, options = {}) {
|
|
|
10025
10097
|
rules.forEach((rule, index) => {
|
|
10026
10098
|
if (rule.effect === "ask") {
|
|
10027
10099
|
if (supersededBy(index, rule.pattern, () => true)) return;
|
|
10028
|
-
if (!
|
|
10100
|
+
if (!ask2.includes(rule.pattern)) ask2.push(rule.pattern);
|
|
10029
10101
|
return;
|
|
10030
10102
|
}
|
|
10031
10103
|
if (rule.effect !== "deny") return;
|
|
@@ -10046,7 +10118,7 @@ function mapReadRules(rules, options = {}) {
|
|
|
10046
10118
|
if (!shadowedAllows.includes(later.pattern)) shadowedAllows.push(later.pattern);
|
|
10047
10119
|
}
|
|
10048
10120
|
});
|
|
10049
|
-
return { deny, ask, unmapped, shadowedAllows };
|
|
10121
|
+
return { deny, ask: ask2, unmapped, shadowedAllows };
|
|
10050
10122
|
}
|
|
10051
10123
|
function resolveReadRuleMapping(input) {
|
|
10052
10124
|
if (input.enabled === false) return emptyMapping(false);
|
|
@@ -10801,6 +10873,159 @@ import * as fs7 from "fs";
|
|
|
10801
10873
|
import * as path10 from "path";
|
|
10802
10874
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10803
10875
|
|
|
10876
|
+
// src/plugin-copies.ts
|
|
10877
|
+
var ROUTED_TURN_LIMIT_MS = 2 * 60 * 6e4;
|
|
10878
|
+
var COPIES_KEY = /* @__PURE__ */ Symbol.for("@khalilgharbaoui/opencode-claude-code-plugin/copies/v1");
|
|
10879
|
+
function copiesState() {
|
|
10880
|
+
const holder = globalThis;
|
|
10881
|
+
let state = holder[COPIES_KEY];
|
|
10882
|
+
if (!state) {
|
|
10883
|
+
state = { copies: [], active: void 0, switching: void 0, turns: /* @__PURE__ */ new Map() };
|
|
10884
|
+
holder[COPIES_KEY] = state;
|
|
10885
|
+
}
|
|
10886
|
+
return state;
|
|
10887
|
+
}
|
|
10888
|
+
function routedTurns(state, now) {
|
|
10889
|
+
for (const [token, turn] of state.turns) {
|
|
10890
|
+
if (now - turn.startedAt > ROUTED_TURN_LIMIT_MS) state.turns.delete(token);
|
|
10891
|
+
}
|
|
10892
|
+
return [...state.turns.values()];
|
|
10893
|
+
}
|
|
10894
|
+
function ask(question, fallback) {
|
|
10895
|
+
try {
|
|
10896
|
+
return question();
|
|
10897
|
+
} catch {
|
|
10898
|
+
return fallback;
|
|
10899
|
+
}
|
|
10900
|
+
}
|
|
10901
|
+
async function releaseQuietly(copy, sessionID) {
|
|
10902
|
+
try {
|
|
10903
|
+
await copy.release(sessionID);
|
|
10904
|
+
} catch (err) {
|
|
10905
|
+
log.warn("an older plugin build failed to release its claude sessions", {
|
|
10906
|
+
version: copy.version,
|
|
10907
|
+
error: err instanceof Error ? err.message : String(err)
|
|
10908
|
+
});
|
|
10909
|
+
}
|
|
10910
|
+
}
|
|
10911
|
+
function registerPluginCopy(copy) {
|
|
10912
|
+
const state = copiesState();
|
|
10913
|
+
if (state.copies.includes(copy)) return;
|
|
10914
|
+
state.copies.push(copy);
|
|
10915
|
+
state.active ??= copy;
|
|
10916
|
+
}
|
|
10917
|
+
function unregisterPluginCopy(copy) {
|
|
10918
|
+
const state = copiesState();
|
|
10919
|
+
const index = state.copies.indexOf(copy);
|
|
10920
|
+
if (index !== -1) state.copies.splice(index, 1);
|
|
10921
|
+
if (state.active === copy) state.active = void 0;
|
|
10922
|
+
}
|
|
10923
|
+
function isActivePluginCopy(copy) {
|
|
10924
|
+
return copiesState().active === copy;
|
|
10925
|
+
}
|
|
10926
|
+
function processLoadedBuild(build) {
|
|
10927
|
+
return copiesState().copies.some(
|
|
10928
|
+
(copy) => !copy.disposed && copy.version === build.version && copy.build.entryPath === build.entryPath && copy.build.mtimeMs === build.mtimeMs && copy.build.size === build.size
|
|
10929
|
+
);
|
|
10930
|
+
}
|
|
10931
|
+
function switchWhenQuiet(state, now) {
|
|
10932
|
+
if (state.switching) return;
|
|
10933
|
+
const newest = [...state.copies].reverse().find((copy) => !copy.disposed);
|
|
10934
|
+
if (!newest || newest === state.active) return;
|
|
10935
|
+
const others = state.copies.filter((copy) => copy !== newest);
|
|
10936
|
+
const busy = new Set(routedTurns(state, now).map((turn) => turn.copy));
|
|
10937
|
+
if (!others.every((copy) => !busy.has(copy) && ask(() => copy.quiet(), false))) return;
|
|
10938
|
+
const previous = state.active;
|
|
10939
|
+
state.active = newest;
|
|
10940
|
+
log.info("switched to the newest plugin build", {
|
|
10941
|
+
from: previous?.version,
|
|
10942
|
+
to: newest.version
|
|
10943
|
+
});
|
|
10944
|
+
state.switching = Promise.all(others.map((copy) => releaseQuietly(copy))).then(() => {
|
|
10945
|
+
state.switching = void 0;
|
|
10946
|
+
});
|
|
10947
|
+
}
|
|
10948
|
+
function servingCopy(state, sessionID, own) {
|
|
10949
|
+
if (sessionID) {
|
|
10950
|
+
const holder = state.copies.find((copy) => ask(() => copy.holds(sessionID), false));
|
|
10951
|
+
if (holder) return holder;
|
|
10952
|
+
}
|
|
10953
|
+
return state.active ?? own;
|
|
10954
|
+
}
|
|
10955
|
+
function followStream(source, end) {
|
|
10956
|
+
const reader = source.getReader();
|
|
10957
|
+
return new ReadableStream({
|
|
10958
|
+
async pull(controller) {
|
|
10959
|
+
try {
|
|
10960
|
+
const { done, value } = await reader.read();
|
|
10961
|
+
if (done) {
|
|
10962
|
+
end();
|
|
10963
|
+
controller.close();
|
|
10964
|
+
return;
|
|
10965
|
+
}
|
|
10966
|
+
controller.enqueue(value);
|
|
10967
|
+
} catch (err) {
|
|
10968
|
+
end();
|
|
10969
|
+
controller.error(err);
|
|
10970
|
+
}
|
|
10971
|
+
},
|
|
10972
|
+
async cancel(reason) {
|
|
10973
|
+
end();
|
|
10974
|
+
await reader.cancel(reason);
|
|
10975
|
+
}
|
|
10976
|
+
});
|
|
10977
|
+
}
|
|
10978
|
+
function untilTurnEnds(result, end) {
|
|
10979
|
+
const stream = result?.stream;
|
|
10980
|
+
if (!(stream instanceof ReadableStream)) {
|
|
10981
|
+
end();
|
|
10982
|
+
return result;
|
|
10983
|
+
}
|
|
10984
|
+
return { ...result, stream: followStream(stream, end) };
|
|
10985
|
+
}
|
|
10986
|
+
async function routeTurn(input) {
|
|
10987
|
+
const state = copiesState();
|
|
10988
|
+
if (state.copies.length === 0) return input.runHere();
|
|
10989
|
+
const now = input.now ?? Date.now;
|
|
10990
|
+
if (state.switching) await state.switching;
|
|
10991
|
+
switchWhenQuiet(state, now());
|
|
10992
|
+
if (state.switching) await state.switching;
|
|
10993
|
+
const sessionID = input.sessionID && input.sessionID !== "default" ? input.sessionID : void 0;
|
|
10994
|
+
const target = servingCopy(state, sessionID, input.own);
|
|
10995
|
+
const token = {};
|
|
10996
|
+
state.turns.set(token, { copy: target, sessionID, startedAt: now() });
|
|
10997
|
+
let ended = false;
|
|
10998
|
+
const end = () => {
|
|
10999
|
+
if (ended) return;
|
|
11000
|
+
ended = true;
|
|
11001
|
+
state.turns.delete(token);
|
|
11002
|
+
};
|
|
11003
|
+
try {
|
|
11004
|
+
if (sessionID) {
|
|
11005
|
+
await Promise.all(
|
|
11006
|
+
state.copies.filter((copy) => copy !== target).map((copy) => releaseQuietly(copy, sessionID))
|
|
11007
|
+
);
|
|
11008
|
+
}
|
|
11009
|
+
if (target === input.own) return untilTurnEnds(await input.runHere(), end);
|
|
11010
|
+
let model;
|
|
11011
|
+
try {
|
|
11012
|
+
model = await target.modelFor(input.turn);
|
|
11013
|
+
} catch (err) {
|
|
11014
|
+
log.warn("the plugin build chosen for this turn could not take it; running it on this one", {
|
|
11015
|
+
version: target.version,
|
|
11016
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11017
|
+
});
|
|
11018
|
+
const turn = state.turns.get(token);
|
|
11019
|
+
if (turn) turn.copy = input.own;
|
|
11020
|
+
return untilTurnEnds(await input.runHere(), end);
|
|
11021
|
+
}
|
|
11022
|
+
return untilTurnEnds(await input.runOn(model), end);
|
|
11023
|
+
} catch (err) {
|
|
11024
|
+
end();
|
|
11025
|
+
throw err;
|
|
11026
|
+
}
|
|
11027
|
+
}
|
|
11028
|
+
|
|
10804
11029
|
// src/startup-diagnostics.ts
|
|
10805
11030
|
import { execFile as execFile3 } from "child_process";
|
|
10806
11031
|
import * as fs6 from "fs";
|
|
@@ -11065,6 +11290,8 @@ function createStaleBuildWatch(io) {
|
|
|
11065
11290
|
const warned = /* @__PURE__ */ new Set();
|
|
11066
11291
|
let checkedAt;
|
|
11067
11292
|
let cached2 = null;
|
|
11293
|
+
let cachedOnDisk;
|
|
11294
|
+
const loadedHere = (onDisk) => onDisk !== void 0 && io.loadedElsewhere?.(onDisk) === true;
|
|
11068
11295
|
const read = () => {
|
|
11069
11296
|
let onDisk;
|
|
11070
11297
|
try {
|
|
@@ -11082,18 +11309,21 @@ function createStaleBuildWatch(io) {
|
|
|
11082
11309
|
},
|
|
11083
11310
|
check() {
|
|
11084
11311
|
const now = io.now();
|
|
11085
|
-
if (checkedAt !== void 0 && now - checkedAt < throttleMs)
|
|
11312
|
+
if (checkedAt !== void 0 && now - checkedAt < throttleMs) {
|
|
11313
|
+
return cached2 && !loadedHere(cachedOnDisk) ? cached2 : null;
|
|
11314
|
+
}
|
|
11086
11315
|
checkedAt = now;
|
|
11087
11316
|
const status = read();
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11317
|
+
cachedOnDisk = status.onDisk;
|
|
11318
|
+
cached2 = status.stale && loadedHere(status.onDisk) ? null : status.stale;
|
|
11319
|
+
if (cached2 && status.onDisk) {
|
|
11320
|
+
const identity = `${cached2.kind}:${status.onDisk.version}:${status.onDisk.mtimeMs}:${status.onDisk.size}`;
|
|
11091
11321
|
if (!warned.has(identity)) {
|
|
11092
11322
|
warned.add(identity);
|
|
11093
11323
|
warn({
|
|
11094
|
-
kind:
|
|
11095
|
-
loadedVersion:
|
|
11096
|
-
onDiskVersion:
|
|
11324
|
+
kind: cached2.kind,
|
|
11325
|
+
loadedVersion: cached2.loadedVersion,
|
|
11326
|
+
onDiskVersion: cached2.onDiskVersion,
|
|
11097
11327
|
path: io.loaded.entryPath
|
|
11098
11328
|
});
|
|
11099
11329
|
}
|
|
@@ -11152,7 +11382,8 @@ function staleBuildWatch() {
|
|
|
11152
11382
|
watch = createStaleBuildWatch({
|
|
11153
11383
|
loaded,
|
|
11154
11384
|
readOnDisk: () => readOnDiskBuild(loaded.entryPath),
|
|
11155
|
-
now: () => Date.now()
|
|
11385
|
+
now: () => Date.now(),
|
|
11386
|
+
loadedElsewhere: (onDisk) => processLoadedBuild({ entryPath: loaded.entryPath, ...onDisk })
|
|
11156
11387
|
});
|
|
11157
11388
|
}
|
|
11158
11389
|
return watch;
|
|
@@ -20900,9 +21131,97 @@ function handleSessionEventHere(event) {
|
|
|
20900
21131
|
const stopped = stoppedSessionIdV2(event);
|
|
20901
21132
|
if (stopped) releaseParkedProxyCallsForSession(stopped);
|
|
20902
21133
|
}
|
|
21134
|
+
var ownCopy;
|
|
21135
|
+
var liveSetups = 0;
|
|
21136
|
+
var plannedByDirectory = /* @__PURE__ */ new Map();
|
|
21137
|
+
var forwardedProviders = /* @__PURE__ */ new Map();
|
|
21138
|
+
var MAX_FORWARDED_PROVIDERS = 32;
|
|
21139
|
+
function plannedSettings(directory, providerID) {
|
|
21140
|
+
const here = plannedByDirectory.get(directory)?.get(providerID);
|
|
21141
|
+
if (here) return here;
|
|
21142
|
+
for (const planned of plannedByDirectory.values()) {
|
|
21143
|
+
const found = planned.get(providerID);
|
|
21144
|
+
if (found) return found;
|
|
21145
|
+
}
|
|
21146
|
+
return void 0;
|
|
21147
|
+
}
|
|
21148
|
+
function pluginCopyFor(deps) {
|
|
21149
|
+
if (ownCopy) return ownCopy;
|
|
21150
|
+
const loaded = staleBuildWatch().loaded;
|
|
21151
|
+
ownCopy = {
|
|
21152
|
+
id: randomUUID8(),
|
|
21153
|
+
version: loaded.version,
|
|
21154
|
+
build: { entryPath: loaded.entryPath, mtimeMs: loaded.mtimeMs, size: loaded.size },
|
|
21155
|
+
disposed: false,
|
|
21156
|
+
holds: (sessionID) => sessionHeldHere(sessionID),
|
|
21157
|
+
quiet: () => noWorkInFlightHere(),
|
|
21158
|
+
release: (sessionID) => releaseIdleForHandover(sessionID),
|
|
21159
|
+
async modelFor(turn) {
|
|
21160
|
+
const planned = plannedSettings(turn.directory, turn.providerID);
|
|
21161
|
+
if (!planned) throw new Error(`no provider ${turn.providerID} is planned in this build`);
|
|
21162
|
+
const cacheKey = JSON.stringify([turn.directory ?? null, turn.providerID, turn.sdkOptions]);
|
|
21163
|
+
let provider = forwardedProviders.get(cacheKey);
|
|
21164
|
+
if (!provider) {
|
|
21165
|
+
const settings = await resolveSdkSettings(turn.providerID, planned, turn.sdkOptions);
|
|
21166
|
+
provider = deps.createProvider(settings);
|
|
21167
|
+
forwardedProviders.set(cacheKey, provider);
|
|
21168
|
+
while (forwardedProviders.size > MAX_FORWARDED_PROVIDERS) {
|
|
21169
|
+
const oldest = forwardedProviders.keys().next().value;
|
|
21170
|
+
if (oldest === void 0) break;
|
|
21171
|
+
forwardedProviders.delete(oldest);
|
|
21172
|
+
}
|
|
21173
|
+
}
|
|
21174
|
+
return provider.languageModel(turn.modelId);
|
|
21175
|
+
}
|
|
21176
|
+
};
|
|
21177
|
+
return ownCopy;
|
|
21178
|
+
}
|
|
21179
|
+
function retireWhenSpent() {
|
|
21180
|
+
const copy = ownCopy;
|
|
21181
|
+
if (!copy?.disposed || isActivePluginCopy(copy) || !holdsNoClaudeState()) return;
|
|
21182
|
+
unregisterPluginCopy(copy);
|
|
21183
|
+
leaveSessionEventRelay();
|
|
21184
|
+
log.info("a disposed plugin build left the process", { version: copy.version });
|
|
21185
|
+
}
|
|
21186
|
+
function routedModel(model, copy, turn) {
|
|
21187
|
+
const sessionOf = (options) => resolveSessionAffinity(
|
|
21188
|
+
options.headers,
|
|
21189
|
+
options.providerOptions,
|
|
21190
|
+
turn.providerID
|
|
21191
|
+
);
|
|
21192
|
+
return {
|
|
21193
|
+
specificationVersion: model.specificationVersion,
|
|
21194
|
+
provider: model.provider,
|
|
21195
|
+
modelId: model.modelId,
|
|
21196
|
+
supportedUrls: model.supportedUrls,
|
|
21197
|
+
doStream: (options) => routeTurn({
|
|
21198
|
+
own: copy,
|
|
21199
|
+
turn,
|
|
21200
|
+
sessionID: sessionOf(options),
|
|
21201
|
+
runHere: () => model.doStream(options),
|
|
21202
|
+
runOn: (other) => other.doStream(options)
|
|
21203
|
+
}),
|
|
21204
|
+
doGenerate: (options) => routeTurn({
|
|
21205
|
+
own: copy,
|
|
21206
|
+
turn,
|
|
21207
|
+
sessionID: sessionOf(options),
|
|
21208
|
+
runHere: () => model.doGenerate(options),
|
|
21209
|
+
runOn: (other) => other.doGenerate(options)
|
|
21210
|
+
})
|
|
21211
|
+
};
|
|
21212
|
+
}
|
|
21213
|
+
function routedProvider(provider, copy, base) {
|
|
21214
|
+
const languageModel = (modelId) => routedModel(provider.languageModel(modelId), copy, { ...base, modelId });
|
|
21215
|
+
return Object.assign((modelId) => languageModel(modelId), provider, {
|
|
21216
|
+
languageModel
|
|
21217
|
+
});
|
|
21218
|
+
}
|
|
20903
21219
|
var closeOwnFeed;
|
|
20904
21220
|
defineOwnRelayMember({
|
|
20905
|
-
handle: (event) =>
|
|
21221
|
+
handle: (event) => {
|
|
21222
|
+
handleSessionEventHere(event);
|
|
21223
|
+
retireWhenSpent();
|
|
21224
|
+
},
|
|
20906
21225
|
feedOpened: () => {
|
|
20907
21226
|
closeOwnFeed ??= openSessionRunEventFeed();
|
|
20908
21227
|
},
|
|
@@ -21015,6 +21334,11 @@ function createV2Setup(deps) {
|
|
|
21015
21334
|
}
|
|
21016
21335
|
}
|
|
21017
21336
|
const planned = /* @__PURE__ */ new Map();
|
|
21337
|
+
const copy = pluginCopyFor(deps);
|
|
21338
|
+
copy.disposed = false;
|
|
21339
|
+
registerPluginCopy(copy);
|
|
21340
|
+
liveSetups += 1;
|
|
21341
|
+
plannedByDirectory.set(directory, planned);
|
|
21018
21342
|
registrations.push(
|
|
21019
21343
|
await ctx.provider.transform((editor) => {
|
|
21020
21344
|
const seed = editor.get(BASE_PROVIDER_ID);
|
|
@@ -21058,7 +21382,11 @@ function createV2Setup(deps) {
|
|
|
21058
21382
|
const providerID = event.model.providerID;
|
|
21059
21383
|
if (!isClaudeCodeProviderId(providerID)) return;
|
|
21060
21384
|
const settings = await resolveSdkSettings(providerID, planned.get(providerID), event.options);
|
|
21061
|
-
event.sdk = deps.createProvider(settings)
|
|
21385
|
+
event.sdk = routedProvider(deps.createProvider(settings), copy, {
|
|
21386
|
+
directory,
|
|
21387
|
+
providerID,
|
|
21388
|
+
sdkOptions: event.options
|
|
21389
|
+
});
|
|
21062
21390
|
log.debug("v2 sdk created", { providerID, cliPath: settings.cliPath });
|
|
21063
21391
|
})
|
|
21064
21392
|
);
|
|
@@ -21125,6 +21453,11 @@ function createV2Setup(deps) {
|
|
|
21125
21453
|
});
|
|
21126
21454
|
return async () => {
|
|
21127
21455
|
events.abort();
|
|
21456
|
+
liveSetups = Math.max(0, liveSetups - 1);
|
|
21457
|
+
if (liveSetups === 0) {
|
|
21458
|
+
copy.disposed = true;
|
|
21459
|
+
if (!relayMemberHasServed()) unregisterPluginCopy(copy);
|
|
21460
|
+
}
|
|
21128
21461
|
for (const registration of registrations.splice(0)) {
|
|
21129
21462
|
await registration.dispose().catch(() => void 0);
|
|
21130
21463
|
}
|