@khalilgharbaoui/opencode-claude-code-plugin 0.62.0 → 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 +474 -61
- 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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/claude-code-language-model.ts
|
|
2
|
-
import { join as
|
|
2
|
+
import { join as join20 } from "path";
|
|
3
3
|
|
|
4
4
|
// src/ids.ts
|
|
5
5
|
import { randomInt } from "crypto";
|
|
@@ -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
|
}
|
|
@@ -7856,6 +7871,77 @@ async function requestSideQuestion(activeProcess, question, options) {
|
|
|
7856
7871
|
});
|
|
7857
7872
|
}
|
|
7858
7873
|
|
|
7874
|
+
// src/session-event-relay.ts
|
|
7875
|
+
var RELAY_KEY = /* @__PURE__ */ Symbol.for("@khalilgharbaoui/opencode-claude-code-plugin/session-event-relay");
|
|
7876
|
+
function relayState() {
|
|
7877
|
+
const holder = globalThis;
|
|
7878
|
+
let state = holder[RELAY_KEY];
|
|
7879
|
+
if (!state) {
|
|
7880
|
+
state = { members: /* @__PURE__ */ new Set(), followers: 0 };
|
|
7881
|
+
holder[RELAY_KEY] = state;
|
|
7882
|
+
}
|
|
7883
|
+
return state;
|
|
7884
|
+
}
|
|
7885
|
+
function safely(action) {
|
|
7886
|
+
try {
|
|
7887
|
+
action();
|
|
7888
|
+
} catch {
|
|
7889
|
+
}
|
|
7890
|
+
}
|
|
7891
|
+
var ownMember;
|
|
7892
|
+
var ownFollowers = 0;
|
|
7893
|
+
var ownServed = false;
|
|
7894
|
+
function join10() {
|
|
7895
|
+
if (!ownMember) return;
|
|
7896
|
+
const state = relayState();
|
|
7897
|
+
if (state.members.has(ownMember)) return;
|
|
7898
|
+
state.members.add(ownMember);
|
|
7899
|
+
if (state.followers > 0) safely(() => ownMember?.feedOpened());
|
|
7900
|
+
}
|
|
7901
|
+
function leave() {
|
|
7902
|
+
if (!ownMember) return;
|
|
7903
|
+
if (relayState().members.delete(ownMember)) safely(() => ownMember?.feedClosed());
|
|
7904
|
+
}
|
|
7905
|
+
function defineOwnRelayMember(member) {
|
|
7906
|
+
ownMember = member;
|
|
7907
|
+
if (ownServed) join10();
|
|
7908
|
+
}
|
|
7909
|
+
function noteRelayMemberServing() {
|
|
7910
|
+
ownServed = true;
|
|
7911
|
+
join10();
|
|
7912
|
+
}
|
|
7913
|
+
function relayMemberHasServed() {
|
|
7914
|
+
return ownServed;
|
|
7915
|
+
}
|
|
7916
|
+
function leaveSessionEventRelay() {
|
|
7917
|
+
leave();
|
|
7918
|
+
}
|
|
7919
|
+
function openRelayFollower() {
|
|
7920
|
+
const state = relayState();
|
|
7921
|
+
join10();
|
|
7922
|
+
ownFollowers += 1;
|
|
7923
|
+
state.followers += 1;
|
|
7924
|
+
if (state.followers === 1) {
|
|
7925
|
+
for (const member of state.members) safely(() => member.feedOpened());
|
|
7926
|
+
}
|
|
7927
|
+
let closed = false;
|
|
7928
|
+
return () => {
|
|
7929
|
+
if (closed) return;
|
|
7930
|
+
closed = true;
|
|
7931
|
+
ownFollowers = Math.max(0, ownFollowers - 1);
|
|
7932
|
+
state.followers = Math.max(0, state.followers - 1);
|
|
7933
|
+
if (state.followers === 0) {
|
|
7934
|
+
for (const member of state.members) safely(() => member.feedClosed());
|
|
7935
|
+
}
|
|
7936
|
+
if (ownFollowers === 0 && !ownServed) leave();
|
|
7937
|
+
};
|
|
7938
|
+
}
|
|
7939
|
+
function relaySessionEvent(event) {
|
|
7940
|
+
const members = relayState().members;
|
|
7941
|
+
if (ownMember && !members.has(ownMember)) safely(() => ownMember?.handle(event));
|
|
7942
|
+
for (const member of [...members]) safely(() => member.handle(event));
|
|
7943
|
+
}
|
|
7944
|
+
|
|
7859
7945
|
// src/session-manager.ts
|
|
7860
7946
|
function findActiveProcessBySessionId(sessionID) {
|
|
7861
7947
|
let found;
|
|
@@ -8082,6 +8168,7 @@ function processBelongsToAnotherAccount(active, cliPath, configDir) {
|
|
|
8082
8168
|
function setActiveProcess(key, ap) {
|
|
8083
8169
|
cancelIdleProcessEviction(key);
|
|
8084
8170
|
activeProcesses.set(key, ap);
|
|
8171
|
+
noteRelayMemberServing();
|
|
8085
8172
|
}
|
|
8086
8173
|
function noteInteractiveProcessExit(sessionKey2, ap, code) {
|
|
8087
8174
|
if (activeProcesses.get(sessionKey2) !== ap) return;
|
|
@@ -8261,6 +8348,10 @@ function setClaudeSessionId(key, sessionId) {
|
|
|
8261
8348
|
capClaudeSessions();
|
|
8262
8349
|
}
|
|
8263
8350
|
function deleteClaudeSessionId(key) {
|
|
8351
|
+
forgetClaudeSessionInMemory(key);
|
|
8352
|
+
forgetResumePoint(key);
|
|
8353
|
+
}
|
|
8354
|
+
function forgetClaudeSessionInMemory(key) {
|
|
8264
8355
|
clearExitPlanModeQuestions(key);
|
|
8265
8356
|
clearAccountFailoverQuestions(key);
|
|
8266
8357
|
clearSubagentDispatchQuestions(key);
|
|
@@ -8269,7 +8360,52 @@ function deleteClaudeSessionId(key) {
|
|
|
8269
8360
|
claudeSessions.delete(key);
|
|
8270
8361
|
forgetForkFingerprint(key);
|
|
8271
8362
|
forgetServedConversation(key);
|
|
8272
|
-
|
|
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;
|
|
8273
8409
|
}
|
|
8274
8410
|
function transferClaudeSession(fromKey, toKey) {
|
|
8275
8411
|
if (fromKey === toKey) return void 0;
|
|
@@ -8376,8 +8512,7 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
|
|
|
8376
8512
|
settleTurn(ap);
|
|
8377
8513
|
lineEmitter.emit("close");
|
|
8378
8514
|
});
|
|
8379
|
-
|
|
8380
|
-
activeProcesses.set(sessionKey2, ap);
|
|
8515
|
+
setActiveProcess(sessionKey2, ap);
|
|
8381
8516
|
proc.on("error", (err) => {
|
|
8382
8517
|
log.error("claude process error", { sessionKey: sessionKey2, error: err.message });
|
|
8383
8518
|
});
|
|
@@ -9101,6 +9236,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
9101
9236
|
"agent maxTurns refused: not a positive whole number",
|
|
9102
9237
|
"agent model override refused: unknown model",
|
|
9103
9238
|
"agent prompt cache ttl refused: unknown value",
|
|
9239
|
+
"an older plugin build failed to release its claude sessions",
|
|
9104
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",
|
|
9105
9241
|
"auto-continuation stopped",
|
|
9106
9242
|
"auto-continuing incomplete claude result",
|
|
@@ -9247,6 +9383,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
9247
9383
|
"task_batch resolving with child results missing",
|
|
9248
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",
|
|
9249
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",
|
|
9250
9387
|
"this opencode process is running an older plugin build than the one on disk",
|
|
9251
9388
|
"timed out pending proxy call",
|
|
9252
9389
|
"turn abort listener threw",
|
|
@@ -9952,7 +10089,7 @@ function emptyMapping(enabled) {
|
|
|
9952
10089
|
}
|
|
9953
10090
|
function mapReadRules(rules, options = {}) {
|
|
9954
10091
|
const deny = [];
|
|
9955
|
-
const
|
|
10092
|
+
const ask2 = [];
|
|
9956
10093
|
const unmapped = [];
|
|
9957
10094
|
const shadowedAllows = [];
|
|
9958
10095
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -9960,7 +10097,7 @@ function mapReadRules(rules, options = {}) {
|
|
|
9960
10097
|
rules.forEach((rule, index) => {
|
|
9961
10098
|
if (rule.effect === "ask") {
|
|
9962
10099
|
if (supersededBy(index, rule.pattern, () => true)) return;
|
|
9963
|
-
if (!
|
|
10100
|
+
if (!ask2.includes(rule.pattern)) ask2.push(rule.pattern);
|
|
9964
10101
|
return;
|
|
9965
10102
|
}
|
|
9966
10103
|
if (rule.effect !== "deny") return;
|
|
@@ -9981,7 +10118,7 @@ function mapReadRules(rules, options = {}) {
|
|
|
9981
10118
|
if (!shadowedAllows.includes(later.pattern)) shadowedAllows.push(later.pattern);
|
|
9982
10119
|
}
|
|
9983
10120
|
});
|
|
9984
|
-
return { deny, ask, unmapped, shadowedAllows };
|
|
10121
|
+
return { deny, ask: ask2, unmapped, shadowedAllows };
|
|
9985
10122
|
}
|
|
9986
10123
|
function resolveReadRuleMapping(input) {
|
|
9987
10124
|
if (input.enabled === false) return emptyMapping(false);
|
|
@@ -10115,20 +10252,20 @@ async function fetchPlanUsage(cliPath, options = {}) {
|
|
|
10115
10252
|
// src/turn-history.ts
|
|
10116
10253
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync6, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
10117
10254
|
import { homedir as homedir11 } from "os";
|
|
10118
|
-
import { join as
|
|
10255
|
+
import { join as join12 } from "path";
|
|
10119
10256
|
|
|
10120
10257
|
// src/turn-progress-store.ts
|
|
10121
10258
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync6, renameSync as renameSync5, statSync as statSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
10122
10259
|
import { homedir as homedir10 } from "os";
|
|
10123
|
-
import { join as
|
|
10260
|
+
import { join as join11 } from "path";
|
|
10124
10261
|
var MAX_TURN_PROGRESS = 64;
|
|
10125
10262
|
var TURN_PROGRESS_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
10126
10263
|
var TAG = /^[0-9a-f]{8}$/;
|
|
10127
10264
|
var pathOverride4 = null;
|
|
10128
10265
|
function turnProgressStorePath() {
|
|
10129
10266
|
if (pathOverride4) return pathOverride4;
|
|
10130
|
-
const stateRoot = process.env.XDG_STATE_HOME ||
|
|
10131
|
-
return
|
|
10267
|
+
const stateRoot = process.env.XDG_STATE_HOME || join11(homedir10(), ".local", "state");
|
|
10268
|
+
return join11(stateRoot, "opencode-claude-code-plugin", "turn-progress.json");
|
|
10132
10269
|
}
|
|
10133
10270
|
function isProgress(value) {
|
|
10134
10271
|
const record = value;
|
|
@@ -10158,7 +10295,7 @@ function recordTurnProgress(tag, progress, now = Date.now()) {
|
|
|
10158
10295
|
merged.set(tag, { ...record, out: Math.round(record.out) });
|
|
10159
10296
|
const kept = [...merged.entries()].slice(-MAX_TURN_PROGRESS);
|
|
10160
10297
|
try {
|
|
10161
|
-
mkdirSync6(
|
|
10298
|
+
mkdirSync6(join11(file, ".."), { recursive: true, mode: 448 });
|
|
10162
10299
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
10163
10300
|
writeFileSync6(tmp, JSON.stringify({ version: 1, turns: Object.fromEntries(kept) }), { mode: 384 });
|
|
10164
10301
|
renameSync5(tmp, file);
|
|
@@ -10176,8 +10313,8 @@ var SESSION_TAG = /^[0-9a-f]{8}$/;
|
|
|
10176
10313
|
var pathOverride5 = null;
|
|
10177
10314
|
function turnHistoryStorePath() {
|
|
10178
10315
|
if (pathOverride5) return pathOverride5;
|
|
10179
|
-
const stateRoot = process.env.XDG_STATE_HOME ||
|
|
10180
|
-
return
|
|
10316
|
+
const stateRoot = process.env.XDG_STATE_HOME || join12(homedir11(), ".local", "state");
|
|
10317
|
+
return join12(stateRoot, "opencode-claude-code-plugin", "turn-history.json");
|
|
10181
10318
|
}
|
|
10182
10319
|
function turnHistoryKey(agent, model) {
|
|
10183
10320
|
return `${agent || "main"}/${model}`;
|
|
@@ -10361,7 +10498,7 @@ function recordTurnSample(key, sample) {
|
|
|
10361
10498
|
merged.set(key, samples);
|
|
10362
10499
|
const kept = bounded2(merged);
|
|
10363
10500
|
try {
|
|
10364
|
-
mkdirSync7(
|
|
10501
|
+
mkdirSync7(join12(file, ".."), { recursive: true, mode: 448 });
|
|
10365
10502
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
10366
10503
|
writeFileSync7(tmp, JSON.stringify({ version: 1, turns: Object.fromEntries(kept) }), { mode: 384 });
|
|
10367
10504
|
renameSync6(tmp, file);
|
|
@@ -10736,6 +10873,159 @@ import * as fs7 from "fs";
|
|
|
10736
10873
|
import * as path10 from "path";
|
|
10737
10874
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10738
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
|
+
|
|
10739
11029
|
// src/startup-diagnostics.ts
|
|
10740
11030
|
import { execFile as execFile3 } from "child_process";
|
|
10741
11031
|
import * as fs6 from "fs";
|
|
@@ -11000,6 +11290,8 @@ function createStaleBuildWatch(io) {
|
|
|
11000
11290
|
const warned = /* @__PURE__ */ new Set();
|
|
11001
11291
|
let checkedAt;
|
|
11002
11292
|
let cached2 = null;
|
|
11293
|
+
let cachedOnDisk;
|
|
11294
|
+
const loadedHere = (onDisk) => onDisk !== void 0 && io.loadedElsewhere?.(onDisk) === true;
|
|
11003
11295
|
const read = () => {
|
|
11004
11296
|
let onDisk;
|
|
11005
11297
|
try {
|
|
@@ -11017,18 +11309,21 @@ function createStaleBuildWatch(io) {
|
|
|
11017
11309
|
},
|
|
11018
11310
|
check() {
|
|
11019
11311
|
const now = io.now();
|
|
11020
|
-
if (checkedAt !== void 0 && now - checkedAt < throttleMs)
|
|
11312
|
+
if (checkedAt !== void 0 && now - checkedAt < throttleMs) {
|
|
11313
|
+
return cached2 && !loadedHere(cachedOnDisk) ? cached2 : null;
|
|
11314
|
+
}
|
|
11021
11315
|
checkedAt = now;
|
|
11022
11316
|
const status = read();
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
|
|
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}`;
|
|
11026
11321
|
if (!warned.has(identity)) {
|
|
11027
11322
|
warned.add(identity);
|
|
11028
11323
|
warn({
|
|
11029
|
-
kind:
|
|
11030
|
-
loadedVersion:
|
|
11031
|
-
onDiskVersion:
|
|
11324
|
+
kind: cached2.kind,
|
|
11325
|
+
loadedVersion: cached2.loadedVersion,
|
|
11326
|
+
onDiskVersion: cached2.onDiskVersion,
|
|
11032
11327
|
path: io.loaded.entryPath
|
|
11033
11328
|
});
|
|
11034
11329
|
}
|
|
@@ -11087,7 +11382,8 @@ function staleBuildWatch() {
|
|
|
11087
11382
|
watch = createStaleBuildWatch({
|
|
11088
11383
|
loaded,
|
|
11089
11384
|
readOnDisk: () => readOnDiskBuild(loaded.entryPath),
|
|
11090
|
-
now: () => Date.now()
|
|
11385
|
+
now: () => Date.now(),
|
|
11386
|
+
loadedElsewhere: (onDisk) => processLoadedBuild({ entryPath: loaded.entryPath, ...onDisk })
|
|
11091
11387
|
});
|
|
11092
11388
|
}
|
|
11093
11389
|
return watch;
|
|
@@ -12785,7 +13081,7 @@ import { EventEmitter as EventEmitter4 } from "events";
|
|
|
12785
13081
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
12786
13082
|
import { existsSync as existsSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
12787
13083
|
import { unlink as unlink2 } from "fs/promises";
|
|
12788
|
-
import { join as
|
|
13084
|
+
import { join as join17 } from "path";
|
|
12789
13085
|
|
|
12790
13086
|
// src/claude-session-bun.ts
|
|
12791
13087
|
import * as os6 from "os";
|
|
@@ -13967,7 +14263,7 @@ ${text2}`
|
|
|
13967
14263
|
}
|
|
13968
14264
|
function stageImage(data, extension) {
|
|
13969
14265
|
try {
|
|
13970
|
-
const file =
|
|
14266
|
+
const file = join17(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
|
|
13971
14267
|
writeFileSync9(file, data, { mode: 384, flag: "wx" });
|
|
13972
14268
|
return file;
|
|
13973
14269
|
} catch (err) {
|
|
@@ -14451,15 +14747,15 @@ function spawnInteractiveProcess(opts) {
|
|
|
14451
14747
|
// src/spawn-record-store.ts
|
|
14452
14748
|
import { mkdirSync as mkdirSync9, readFileSync as readFileSync11, renameSync as renameSync7, statSync as statSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
14453
14749
|
import { homedir as homedir14 } from "os";
|
|
14454
|
-
import { join as
|
|
14750
|
+
import { join as join18 } from "path";
|
|
14455
14751
|
var MAX_SPAWN_RECORDS = 256;
|
|
14456
14752
|
var SPAWN_RECORD_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
14457
14753
|
var pathOverride6 = null;
|
|
14458
14754
|
var lastWritten = /* @__PURE__ */ new Map();
|
|
14459
14755
|
function spawnRecordStorePath() {
|
|
14460
14756
|
if (pathOverride6) return pathOverride6;
|
|
14461
|
-
const stateRoot = process.env.XDG_STATE_HOME ||
|
|
14462
|
-
return
|
|
14757
|
+
const stateRoot = process.env.XDG_STATE_HOME || join18(homedir14(), ".local", "state");
|
|
14758
|
+
return join18(stateRoot, "opencode-claude-code-plugin", "session-spawns.json");
|
|
14463
14759
|
}
|
|
14464
14760
|
function isRecord5(value) {
|
|
14465
14761
|
const record = value;
|
|
@@ -14511,7 +14807,7 @@ ${record.agent ?? ""}`;
|
|
|
14511
14807
|
const cutoff = now - SPAWN_RECORD_MAX_AGE_MS;
|
|
14512
14808
|
const kept = [...merged.entries()].filter(([, entry]) => entry.at >= cutoff).sort((a, b) => a[1].at - b[1].at).slice(-MAX_SPAWN_RECORDS);
|
|
14513
14809
|
try {
|
|
14514
|
-
mkdirSync9(
|
|
14810
|
+
mkdirSync9(join18(file, ".."), { recursive: true, mode: 448 });
|
|
14515
14811
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
14516
14812
|
writeFileSync10(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {
|
|
14517
14813
|
mode: 384
|
|
@@ -14589,7 +14885,7 @@ function dedupe2(dirs) {
|
|
|
14589
14885
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
|
|
14590
14886
|
import { homedir as homedir15 } from "os";
|
|
14591
14887
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
14592
|
-
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as
|
|
14888
|
+
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join19 } from "path";
|
|
14593
14889
|
function readPromptFileIfPresent(path15) {
|
|
14594
14890
|
try {
|
|
14595
14891
|
const content = readFileSync12(path15, "utf8").trim();
|
|
@@ -14601,7 +14897,7 @@ function readPromptFileIfPresent(path15) {
|
|
|
14601
14897
|
function nearestWorkspaceAgentsPrompt(cwd) {
|
|
14602
14898
|
let dir = cwd;
|
|
14603
14899
|
while (true) {
|
|
14604
|
-
const content = readPromptFileIfPresent(
|
|
14900
|
+
const content = readPromptFileIfPresent(join19(dir, "AGENTS.md"));
|
|
14605
14901
|
if (content) return content;
|
|
14606
14902
|
const parent = dirname6(dir);
|
|
14607
14903
|
if (parent === dir) return void 0;
|
|
@@ -14753,8 +15049,8 @@ ${options.compressionSummary.trim()}`
|
|
|
14753
15049
|
for (const s of extraSystemContent) {
|
|
14754
15050
|
if (s.trim()) parts.push(s.trim());
|
|
14755
15051
|
}
|
|
14756
|
-
const configRoot = process.env.XDG_CONFIG_HOME ??
|
|
14757
|
-
const globalAgents = readPromptFileIfPresent(
|
|
15052
|
+
const configRoot = process.env.XDG_CONFIG_HOME ?? join19(homedir15(), ".config");
|
|
15053
|
+
const globalAgents = readPromptFileIfPresent(join19(configRoot, "opencode", "AGENTS.md"));
|
|
14758
15054
|
const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
|
|
14759
15055
|
const forwarded = extraSystemContent.join("\n\n");
|
|
14760
15056
|
const pushGlobal = !!globalAgents && !forwarded.includes(globalAgents);
|
|
@@ -14770,7 +15066,7 @@ ${options.compressionSummary.trim()}`
|
|
|
14770
15066
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
14771
15067
|
const content = parts.join("\n\n");
|
|
14772
15068
|
if (!content) return void 0;
|
|
14773
|
-
const path15 =
|
|
15069
|
+
const path15 = join19(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
|
|
14774
15070
|
try {
|
|
14775
15071
|
writeFileSync11(path15, content, { encoding: "utf8", mode: 384 });
|
|
14776
15072
|
return path15;
|
|
@@ -18614,7 +18910,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18614
18910
|
let replayReason = "no-session-for-key";
|
|
18615
18911
|
if (this.config.resumeAfterRestart !== false && includeHistoryContext && !compactionMode && !forkFromClaudeSessionId && failoverAnswer?.kind !== "switch") {
|
|
18616
18912
|
const configDir = resolveConfigDir(this.config.configDir);
|
|
18617
|
-
const transcriptPath2 = (id, dir) =>
|
|
18913
|
+
const transcriptPath2 = (id, dir) => join20(dir ?? configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
|
|
18618
18914
|
const resumePoint = findResumePoint({
|
|
18619
18915
|
sessionKey: sk,
|
|
18620
18916
|
prompt: options.prompt,
|
|
@@ -20289,7 +20585,7 @@ import {
|
|
|
20289
20585
|
writeFileSync as writeFileSync12
|
|
20290
20586
|
} from "fs";
|
|
20291
20587
|
import { homedir as homedir16 } from "os";
|
|
20292
|
-
import { dirname as dirname7, join as
|
|
20588
|
+
import { dirname as dirname7, join as join21, resolve as resolve6 } from "path";
|
|
20293
20589
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
20294
20590
|
var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
|
|
20295
20591
|
var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
|
|
@@ -20297,14 +20593,14 @@ var alreadyRan = false;
|
|
|
20297
20593
|
function candidateCacheRoots() {
|
|
20298
20594
|
const xdg = process.env.XDG_CACHE_HOME;
|
|
20299
20595
|
return [
|
|
20300
|
-
xdg ?
|
|
20301
|
-
|
|
20302
|
-
|
|
20596
|
+
xdg ? join21(xdg, "opencode") : null,
|
|
20597
|
+
join21(homedir16(), ".cache", "opencode"),
|
|
20598
|
+
join21(homedir16(), "Library", "Caches", "opencode")
|
|
20303
20599
|
].filter((p) => Boolean(p));
|
|
20304
20600
|
}
|
|
20305
20601
|
function userOpencodeJsonPath() {
|
|
20306
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ??
|
|
20307
|
-
return
|
|
20602
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join21(homedir16(), ".config");
|
|
20603
|
+
return join21(xdgConfig, "opencode", "opencode.json");
|
|
20308
20604
|
}
|
|
20309
20605
|
function userIntendsToUseUnscoped() {
|
|
20310
20606
|
const cfg = userOpencodeJsonPath();
|
|
@@ -20331,15 +20627,15 @@ function ourLoadedDir() {
|
|
|
20331
20627
|
function ourVersion(dir) {
|
|
20332
20628
|
if (!dir) return null;
|
|
20333
20629
|
try {
|
|
20334
|
-
const pkg = JSON.parse(readFileSync14(
|
|
20630
|
+
const pkg = JSON.parse(readFileSync14(join21(dir, "package.json"), "utf8"));
|
|
20335
20631
|
return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
|
|
20336
20632
|
} catch {
|
|
20337
20633
|
return null;
|
|
20338
20634
|
}
|
|
20339
20635
|
}
|
|
20340
20636
|
function markerPath() {
|
|
20341
|
-
const stateRoot = process.env.XDG_STATE_HOME ??
|
|
20342
|
-
return
|
|
20637
|
+
const stateRoot = process.env.XDG_STATE_HOME ?? join21(homedir16(), ".local", "state");
|
|
20638
|
+
return join21(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
|
|
20343
20639
|
}
|
|
20344
20640
|
function markerVersion() {
|
|
20345
20641
|
try {
|
|
@@ -20388,7 +20684,7 @@ function cleanupStaleUnscopedInstall() {
|
|
|
20388
20684
|
}
|
|
20389
20685
|
function cleanupOne(cacheRoot, ourDir) {
|
|
20390
20686
|
if (!existsSync5(cacheRoot)) return;
|
|
20391
|
-
const stalePath =
|
|
20687
|
+
const stalePath = join21(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
|
|
20392
20688
|
if (!existsSync5(stalePath)) return;
|
|
20393
20689
|
let realStalePath = stalePath;
|
|
20394
20690
|
try {
|
|
@@ -20396,7 +20692,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
20396
20692
|
} catch {
|
|
20397
20693
|
}
|
|
20398
20694
|
if (ourDir && realStalePath === ourDir) return;
|
|
20399
|
-
const pkgJsonPath =
|
|
20695
|
+
const pkgJsonPath = join21(stalePath, "package.json");
|
|
20400
20696
|
if (!existsSync5(pkgJsonPath)) return;
|
|
20401
20697
|
let pkg = {};
|
|
20402
20698
|
try {
|
|
@@ -20416,7 +20712,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
20416
20712
|
});
|
|
20417
20713
|
return;
|
|
20418
20714
|
}
|
|
20419
|
-
const cachePkgJson =
|
|
20715
|
+
const cachePkgJson = join21(cacheRoot, "package.json");
|
|
20420
20716
|
if (!existsSync5(cachePkgJson)) return;
|
|
20421
20717
|
try {
|
|
20422
20718
|
const cfg = JSON.parse(readFileSync14(cachePkgJson, "utf8"));
|
|
@@ -20822,22 +21118,125 @@ function stoppedSessionIdV2(event) {
|
|
|
20822
21118
|
const id = event.data?.sessionID;
|
|
20823
21119
|
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
20824
21120
|
}
|
|
21121
|
+
function handleSessionEventHere(event) {
|
|
21122
|
+
noteSessionRunEvent(event);
|
|
21123
|
+
const deleted = deletedSessionIdV2(event);
|
|
21124
|
+
if (deleted) {
|
|
21125
|
+
const released = deleteActiveProcessesForSession(deleted);
|
|
21126
|
+
if (released.length > 0) {
|
|
21127
|
+
log.info("released claude state for deleted session", { sessionID: deleted, released });
|
|
21128
|
+
}
|
|
21129
|
+
return;
|
|
21130
|
+
}
|
|
21131
|
+
const stopped = stoppedSessionIdV2(event);
|
|
21132
|
+
if (stopped) releaseParkedProxyCallsForSession(stopped);
|
|
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
|
+
}
|
|
21219
|
+
var closeOwnFeed;
|
|
21220
|
+
defineOwnRelayMember({
|
|
21221
|
+
handle: (event) => {
|
|
21222
|
+
handleSessionEventHere(event);
|
|
21223
|
+
retireWhenSpent();
|
|
21224
|
+
},
|
|
21225
|
+
feedOpened: () => {
|
|
21226
|
+
closeOwnFeed ??= openSessionRunEventFeed();
|
|
21227
|
+
},
|
|
21228
|
+
feedClosed: () => {
|
|
21229
|
+
const close = closeOwnFeed;
|
|
21230
|
+
closeOwnFeed = void 0;
|
|
21231
|
+
close?.();
|
|
21232
|
+
}
|
|
21233
|
+
});
|
|
20825
21234
|
async function followSessionEvents(subscribe, signal) {
|
|
20826
|
-
const
|
|
21235
|
+
const closeFollower = openRelayFollower();
|
|
20827
21236
|
try {
|
|
20828
21237
|
for await (const event of subscribe({ signal })) {
|
|
20829
21238
|
if (signal.aborted) break;
|
|
20830
|
-
|
|
20831
|
-
const deleted = deletedSessionIdV2(event);
|
|
20832
|
-
if (deleted) {
|
|
20833
|
-
const released = deleteActiveProcessesForSession(deleted);
|
|
20834
|
-
if (released.length > 0) {
|
|
20835
|
-
log.info("released claude state for deleted session", { sessionID: deleted, released });
|
|
20836
|
-
}
|
|
20837
|
-
continue;
|
|
20838
|
-
}
|
|
20839
|
-
const stopped = stoppedSessionIdV2(event);
|
|
20840
|
-
if (stopped) releaseParkedProxyCallsForSession(stopped);
|
|
21239
|
+
relaySessionEvent(event);
|
|
20841
21240
|
}
|
|
20842
21241
|
} catch (err) {
|
|
20843
21242
|
if (!signal.aborted) {
|
|
@@ -20847,7 +21246,7 @@ async function followSessionEvents(subscribe, signal) {
|
|
|
20847
21246
|
);
|
|
20848
21247
|
}
|
|
20849
21248
|
} finally {
|
|
20850
|
-
|
|
21249
|
+
closeFollower();
|
|
20851
21250
|
}
|
|
20852
21251
|
}
|
|
20853
21252
|
var noticeEmitters = /* @__PURE__ */ new Set();
|
|
@@ -20935,6 +21334,11 @@ function createV2Setup(deps) {
|
|
|
20935
21334
|
}
|
|
20936
21335
|
}
|
|
20937
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);
|
|
20938
21342
|
registrations.push(
|
|
20939
21343
|
await ctx.provider.transform((editor) => {
|
|
20940
21344
|
const seed = editor.get(BASE_PROVIDER_ID);
|
|
@@ -20978,7 +21382,11 @@ function createV2Setup(deps) {
|
|
|
20978
21382
|
const providerID = event.model.providerID;
|
|
20979
21383
|
if (!isClaudeCodeProviderId(providerID)) return;
|
|
20980
21384
|
const settings = await resolveSdkSettings(providerID, planned.get(providerID), event.options);
|
|
20981
|
-
event.sdk = deps.createProvider(settings)
|
|
21385
|
+
event.sdk = routedProvider(deps.createProvider(settings), copy, {
|
|
21386
|
+
directory,
|
|
21387
|
+
providerID,
|
|
21388
|
+
sdkOptions: event.options
|
|
21389
|
+
});
|
|
20982
21390
|
log.debug("v2 sdk created", { providerID, cliPath: settings.cliPath });
|
|
20983
21391
|
})
|
|
20984
21392
|
);
|
|
@@ -21045,6 +21453,11 @@ function createV2Setup(deps) {
|
|
|
21045
21453
|
});
|
|
21046
21454
|
return async () => {
|
|
21047
21455
|
events.abort();
|
|
21456
|
+
liveSetups = Math.max(0, liveSetups - 1);
|
|
21457
|
+
if (liveSetups === 0) {
|
|
21458
|
+
copy.disposed = true;
|
|
21459
|
+
if (!relayMemberHasServed()) unregisterPluginCopy(copy);
|
|
21460
|
+
}
|
|
21048
21461
|
for (const registration of registrations.splice(0)) {
|
|
21049
21462
|
await registration.dispose().catch(() => void 0);
|
|
21050
21463
|
}
|