@khalilgharbaoui/opencode-claude-code-plugin 0.4.1 → 0.4.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.js +189 -62
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1319,52 +1319,54 @@ function writeJson(res, body) {
|
|
|
1319
1319
|
|
|
1320
1320
|
// src/proxy-broker.ts
|
|
1321
1321
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
1322
|
-
var
|
|
1322
|
+
var pendingByCallId = /* @__PURE__ */ new Map();
|
|
1323
|
+
var callIdsBySession = /* @__PURE__ */ new Map();
|
|
1323
1324
|
var emitter = new EventEmitter3();
|
|
1324
1325
|
var PENDING_PROXY_CALL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
1325
1326
|
function eventName(sessionKey2) {
|
|
1326
1327
|
return `pending:${sessionKey2}`;
|
|
1327
1328
|
}
|
|
1329
|
+
function indexAdd(sessionKey2, callId) {
|
|
1330
|
+
let s = callIdsBySession.get(sessionKey2);
|
|
1331
|
+
if (!s) {
|
|
1332
|
+
s = /* @__PURE__ */ new Set();
|
|
1333
|
+
callIdsBySession.set(sessionKey2, s);
|
|
1334
|
+
}
|
|
1335
|
+
s.add(callId);
|
|
1336
|
+
}
|
|
1337
|
+
function indexRemove(sessionKey2, callId) {
|
|
1338
|
+
const s = callIdsBySession.get(sessionKey2);
|
|
1339
|
+
if (!s) return;
|
|
1340
|
+
s.delete(callId);
|
|
1341
|
+
if (s.size === 0) callIdsBySession.delete(sessionKey2);
|
|
1342
|
+
}
|
|
1328
1343
|
function onPendingProxyCall(sessionKey2, handler) {
|
|
1329
1344
|
const name = eventName(sessionKey2);
|
|
1330
1345
|
emitter.on(name, handler);
|
|
1331
1346
|
return () => emitter.off(name, handler);
|
|
1332
1347
|
}
|
|
1333
1348
|
function queuePendingProxyCall(sessionKey2, call) {
|
|
1334
|
-
const
|
|
1335
|
-
if (
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
);
|
|
1340
|
-
log.warn("rejected overlapping proxy call", {
|
|
1341
|
-
sessionKey: sessionKey2,
|
|
1342
|
-
existingToolCallId: existing.toolCallId,
|
|
1343
|
-
existingToolName: existing.toolName,
|
|
1344
|
-
toolCallId: call.id,
|
|
1345
|
-
toolName: call.toolName
|
|
1346
|
-
});
|
|
1347
|
-
return existing;
|
|
1348
|
-
}
|
|
1349
|
-
clearTimeout(existing.timer);
|
|
1350
|
-
existing.reject(
|
|
1351
|
-
new Error(
|
|
1352
|
-
`Stale proxy tool call expired after ${PENDING_PROXY_CALL_TIMEOUT_MS}ms for ${sessionKey2}`
|
|
1353
|
-
)
|
|
1349
|
+
const previous = pendingByCallId.get(call.id);
|
|
1350
|
+
if (previous) {
|
|
1351
|
+
clearTimeout(previous.timer);
|
|
1352
|
+
previous.reject(
|
|
1353
|
+
new Error(`Replaced pending proxy call ${call.id} with a fresh one`)
|
|
1354
1354
|
);
|
|
1355
|
-
|
|
1355
|
+
pendingByCallId.delete(call.id);
|
|
1356
|
+
indexRemove(previous.sessionKey, call.id);
|
|
1356
1357
|
}
|
|
1357
1358
|
const timer = setTimeout(() => {
|
|
1358
|
-
const current =
|
|
1359
|
-
if (!current
|
|
1360
|
-
|
|
1359
|
+
const current = pendingByCallId.get(call.id);
|
|
1360
|
+
if (!current) return;
|
|
1361
|
+
pendingByCallId.delete(call.id);
|
|
1362
|
+
indexRemove(current.sessionKey, call.id);
|
|
1361
1363
|
current.reject(
|
|
1362
1364
|
new Error(
|
|
1363
1365
|
`Proxy tool call '${call.toolName}' timed out after ${PENDING_PROXY_CALL_TIMEOUT_MS}ms waiting for opencode to resolve the call`
|
|
1364
1366
|
)
|
|
1365
1367
|
);
|
|
1366
1368
|
log.warn("timed out pending proxy call", {
|
|
1367
|
-
sessionKey:
|
|
1369
|
+
sessionKey: current.sessionKey,
|
|
1368
1370
|
toolCallId: call.id,
|
|
1369
1371
|
toolName: call.toolName,
|
|
1370
1372
|
timeoutMs: PENDING_PROXY_CALL_TIMEOUT_MS
|
|
@@ -1380,7 +1382,8 @@ function queuePendingProxyCall(sessionKey2, call) {
|
|
|
1380
1382
|
resolve: call.resolve,
|
|
1381
1383
|
reject: call.reject
|
|
1382
1384
|
};
|
|
1383
|
-
|
|
1385
|
+
pendingByCallId.set(call.id, pending);
|
|
1386
|
+
indexAdd(sessionKey2, call.id);
|
|
1384
1387
|
emitter.emit(eventName(sessionKey2), pending);
|
|
1385
1388
|
log.info("queued pending proxy call", {
|
|
1386
1389
|
sessionKey: sessionKey2,
|
|
@@ -1389,22 +1392,55 @@ function queuePendingProxyCall(sessionKey2, call) {
|
|
|
1389
1392
|
});
|
|
1390
1393
|
return pending;
|
|
1391
1394
|
}
|
|
1392
|
-
function
|
|
1393
|
-
|
|
1395
|
+
function getPendingProxyCalls(sessionKey2) {
|
|
1396
|
+
const s = callIdsBySession.get(sessionKey2);
|
|
1397
|
+
if (!s || s.size === 0) return [];
|
|
1398
|
+
const out = [];
|
|
1399
|
+
for (const id of s) {
|
|
1400
|
+
const p = pendingByCallId.get(id);
|
|
1401
|
+
if (p) out.push(p);
|
|
1402
|
+
}
|
|
1403
|
+
return out;
|
|
1394
1404
|
}
|
|
1395
|
-
function
|
|
1396
|
-
const pending =
|
|
1405
|
+
function resolvePendingProxyCallById(toolCallId, result) {
|
|
1406
|
+
const pending = pendingByCallId.get(toolCallId);
|
|
1397
1407
|
if (!pending) return false;
|
|
1398
|
-
|
|
1408
|
+
pendingByCallId.delete(toolCallId);
|
|
1409
|
+
indexRemove(pending.sessionKey, toolCallId);
|
|
1399
1410
|
clearTimeout(pending.timer);
|
|
1400
1411
|
pending.resolve(result);
|
|
1401
1412
|
log.info("resolved pending proxy call", {
|
|
1402
|
-
sessionKey:
|
|
1413
|
+
sessionKey: pending.sessionKey,
|
|
1403
1414
|
toolCallId: pending.toolCallId,
|
|
1404
1415
|
toolName: pending.toolName
|
|
1405
1416
|
});
|
|
1406
1417
|
return true;
|
|
1407
1418
|
}
|
|
1419
|
+
function rejectPendingProxyCallById(toolCallId, error) {
|
|
1420
|
+
const pending = pendingByCallId.get(toolCallId);
|
|
1421
|
+
if (!pending) return false;
|
|
1422
|
+
pendingByCallId.delete(toolCallId);
|
|
1423
|
+
indexRemove(pending.sessionKey, toolCallId);
|
|
1424
|
+
clearTimeout(pending.timer);
|
|
1425
|
+
pending.reject(error);
|
|
1426
|
+
log.warn("rejected pending proxy call", {
|
|
1427
|
+
sessionKey: pending.sessionKey,
|
|
1428
|
+
toolCallId: pending.toolCallId,
|
|
1429
|
+
toolName: pending.toolName,
|
|
1430
|
+
error: error.message
|
|
1431
|
+
});
|
|
1432
|
+
return true;
|
|
1433
|
+
}
|
|
1434
|
+
function rejectAllPendingProxyCallsForSession(sessionKey2, error) {
|
|
1435
|
+
const s = callIdsBySession.get(sessionKey2);
|
|
1436
|
+
if (!s) return 0;
|
|
1437
|
+
const ids = [...s];
|
|
1438
|
+
let count = 0;
|
|
1439
|
+
for (const id of ids) {
|
|
1440
|
+
if (rejectPendingProxyCallById(id, error)) count++;
|
|
1441
|
+
}
|
|
1442
|
+
return count;
|
|
1443
|
+
}
|
|
1408
1444
|
|
|
1409
1445
|
// src/claude-code-language-model.ts
|
|
1410
1446
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -2238,8 +2274,14 @@ ${plan}
|
|
|
2238
2274
|
);
|
|
2239
2275
|
const resolvedProxy = this.resolvedProxyTools();
|
|
2240
2276
|
const self = this;
|
|
2241
|
-
const
|
|
2242
|
-
const
|
|
2277
|
+
const previousPendingProxyCalls = getPendingProxyCalls(sk);
|
|
2278
|
+
const previousPendingProxyMatches = previousPendingProxyCalls.map((call) => ({
|
|
2279
|
+
call,
|
|
2280
|
+
result: this.extractPendingProxyResult(options.prompt, call.toolCallId)
|
|
2281
|
+
}));
|
|
2282
|
+
const hasMatchedPendingResults = previousPendingProxyMatches.some(
|
|
2283
|
+
(m) => m.result !== null
|
|
2284
|
+
);
|
|
2243
2285
|
const runtimeStatus = await getRuntimeMcpStatus();
|
|
2244
2286
|
log.info("doStream starting", {
|
|
2245
2287
|
cwd,
|
|
@@ -2369,21 +2411,27 @@ ${plan}
|
|
|
2369
2411
|
const skipResultForIds = /* @__PURE__ */ new Set();
|
|
2370
2412
|
const toolCallsById = /* @__PURE__ */ new Map();
|
|
2371
2413
|
let resultMeta = {};
|
|
2372
|
-
const
|
|
2414
|
+
const drainBuffer = [];
|
|
2415
|
+
let drainTimer = null;
|
|
2416
|
+
const DRAIN_QUIET_MS = 100;
|
|
2417
|
+
const finishWithToolCalls = (calls) => {
|
|
2373
2418
|
if (controllerClosed) return;
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2419
|
+
if (calls.length === 0) return;
|
|
2420
|
+
for (const call of calls) {
|
|
2421
|
+
controller.enqueue({
|
|
2422
|
+
type: "tool-input-start",
|
|
2423
|
+
id: call.toolCallId,
|
|
2424
|
+
toolName: call.toolName
|
|
2425
|
+
});
|
|
2426
|
+
controller.enqueue({
|
|
2427
|
+
type: "tool-call",
|
|
2428
|
+
toolCallId: call.toolCallId,
|
|
2429
|
+
toolName: call.toolName,
|
|
2430
|
+
input: JSON.stringify(call.input),
|
|
2431
|
+
providerExecuted: false
|
|
2432
|
+
});
|
|
2433
|
+
skipResultForIds.add(call.toolCallId);
|
|
2434
|
+
}
|
|
2387
2435
|
controller.enqueue({
|
|
2388
2436
|
type: "finish",
|
|
2389
2437
|
finishReason: toFinishReason("tool-calls"),
|
|
@@ -2399,6 +2447,21 @@ ${plan}
|
|
|
2399
2447
|
} catch {
|
|
2400
2448
|
}
|
|
2401
2449
|
};
|
|
2450
|
+
const drainNow = () => {
|
|
2451
|
+
if (drainTimer) {
|
|
2452
|
+
clearTimeout(drainTimer);
|
|
2453
|
+
drainTimer = null;
|
|
2454
|
+
}
|
|
2455
|
+
if (drainBuffer.length === 0) return;
|
|
2456
|
+
if (controllerClosed) return;
|
|
2457
|
+
const batch = drainBuffer.splice(0, drainBuffer.length);
|
|
2458
|
+
log.info("draining pending proxy calls into stream finish", {
|
|
2459
|
+
sessionKey: sk,
|
|
2460
|
+
count: batch.length,
|
|
2461
|
+
toolCallIds: batch.map((c) => c.toolCallId)
|
|
2462
|
+
});
|
|
2463
|
+
finishWithToolCalls(batch);
|
|
2464
|
+
};
|
|
2402
2465
|
let gotPartialEvents = false;
|
|
2403
2466
|
const lineHandler = (line) => {
|
|
2404
2467
|
if (!line.trim()) return;
|
|
@@ -2831,6 +2894,15 @@ ${plan}
|
|
|
2831
2894
|
const closeHandler = () => {
|
|
2832
2895
|
log.debug("readline closed");
|
|
2833
2896
|
if (controllerClosed) return;
|
|
2897
|
+
if (drainBuffer.length > 0 || getPendingProxyCalls(sk).length > 0) {
|
|
2898
|
+
rejectAllPendingProxyCallsForSession(
|
|
2899
|
+
sk,
|
|
2900
|
+
new Error(
|
|
2901
|
+
"Claude CLI subprocess closed before pending tool calls were resolved"
|
|
2902
|
+
)
|
|
2903
|
+
);
|
|
2904
|
+
drainBuffer.length = 0;
|
|
2905
|
+
}
|
|
2834
2906
|
controllerClosed = true;
|
|
2835
2907
|
cleanupTurn();
|
|
2836
2908
|
endTextBlock();
|
|
@@ -2852,6 +2924,10 @@ ${plan}
|
|
|
2852
2924
|
if (cleanedUp) return;
|
|
2853
2925
|
cleanedUp = true;
|
|
2854
2926
|
clearFallbackTimer();
|
|
2927
|
+
if (drainTimer) {
|
|
2928
|
+
clearTimeout(drainTimer);
|
|
2929
|
+
drainTimer = null;
|
|
2930
|
+
}
|
|
2855
2931
|
lineEmitter.off("line", lineHandler);
|
|
2856
2932
|
lineEmitter.off("close", closeHandler);
|
|
2857
2933
|
pendingProxyUnsubscribe?.();
|
|
@@ -2861,6 +2937,15 @@ ${plan}
|
|
|
2861
2937
|
const procErrorHandler = (err) => {
|
|
2862
2938
|
log.error("process error", { error: err.message });
|
|
2863
2939
|
if (controllerClosed) return;
|
|
2940
|
+
if (drainBuffer.length > 0 || getPendingProxyCalls(sk).length > 0) {
|
|
2941
|
+
rejectAllPendingProxyCallsForSession(
|
|
2942
|
+
sk,
|
|
2943
|
+
new Error(
|
|
2944
|
+
`Claude CLI subprocess error: ${err.message}`
|
|
2945
|
+
)
|
|
2946
|
+
);
|
|
2947
|
+
drainBuffer.length = 0;
|
|
2948
|
+
}
|
|
2864
2949
|
controllerClosed = true;
|
|
2865
2950
|
cleanupTurn();
|
|
2866
2951
|
controller.enqueue({ type: "error", error: err });
|
|
@@ -2872,12 +2957,31 @@ ${plan}
|
|
|
2872
2957
|
lineEmitter.on("line", lineHandler);
|
|
2873
2958
|
lineEmitter.on("close", closeHandler);
|
|
2874
2959
|
pendingProxyUnsubscribe = onPendingProxyCall(sk, (call) => {
|
|
2960
|
+
if (controllerClosed) {
|
|
2961
|
+
log.warn(
|
|
2962
|
+
"pending proxy call arrived after stream close; rejecting",
|
|
2963
|
+
{
|
|
2964
|
+
sessionKey: sk,
|
|
2965
|
+
toolCallId: call.toolCallId,
|
|
2966
|
+
toolName: call.toolName
|
|
2967
|
+
}
|
|
2968
|
+
);
|
|
2969
|
+
rejectPendingProxyCallById(
|
|
2970
|
+
call.toolCallId,
|
|
2971
|
+
new Error(
|
|
2972
|
+
`Pending proxy call '${call.toolName}' arrived after the stream was already closed`
|
|
2973
|
+
)
|
|
2974
|
+
);
|
|
2975
|
+
return;
|
|
2976
|
+
}
|
|
2875
2977
|
log.info("received pending proxy call for session", {
|
|
2876
2978
|
sessionKey: sk,
|
|
2877
2979
|
toolCallId: call.toolCallId,
|
|
2878
2980
|
toolName: call.toolName
|
|
2879
2981
|
});
|
|
2880
|
-
|
|
2982
|
+
drainBuffer.push(call);
|
|
2983
|
+
if (drainTimer) clearTimeout(drainTimer);
|
|
2984
|
+
drainTimer = setTimeout(drainNow, DRAIN_QUIET_MS);
|
|
2881
2985
|
});
|
|
2882
2986
|
proc.on("error", procErrorHandler);
|
|
2883
2987
|
if (options.abortSignal) {
|
|
@@ -2903,21 +3007,44 @@ ${plan}
|
|
|
2903
3007
|
startResultFallback(5e3);
|
|
2904
3008
|
});
|
|
2905
3009
|
}
|
|
2906
|
-
if (
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
3010
|
+
if (hasMatchedPendingResults) {
|
|
3011
|
+
for (const { call, result } of previousPendingProxyMatches) {
|
|
3012
|
+
if (result) {
|
|
3013
|
+
log.info("resolving pending proxy call from tool result prompt", {
|
|
3014
|
+
sessionKey: sk,
|
|
3015
|
+
toolCallId: call.toolCallId,
|
|
3016
|
+
toolName: call.toolName
|
|
3017
|
+
});
|
|
3018
|
+
resolvePendingProxyCallById(call.toolCallId, result);
|
|
3019
|
+
} else {
|
|
3020
|
+
log.warn(
|
|
3021
|
+
"pending proxy call had no matching tool-result; rejecting as orphan",
|
|
3022
|
+
{
|
|
3023
|
+
sessionKey: sk,
|
|
3024
|
+
toolCallId: call.toolCallId,
|
|
3025
|
+
toolName: call.toolName
|
|
3026
|
+
}
|
|
3027
|
+
);
|
|
3028
|
+
rejectPendingProxyCallById(
|
|
3029
|
+
call.toolCallId,
|
|
3030
|
+
new Error(
|
|
3031
|
+
`Pending proxy call '${call.toolName}' (${call.toolCallId}) was not matched in tool-result turn; rejecting as orphaned`
|
|
3032
|
+
)
|
|
3033
|
+
);
|
|
3034
|
+
}
|
|
2918
3035
|
}
|
|
2919
3036
|
return;
|
|
2920
3037
|
}
|
|
3038
|
+
if (previousPendingProxyCalls.length > 0) {
|
|
3039
|
+
for (const call of previousPendingProxyCalls) {
|
|
3040
|
+
rejectPendingProxyCallById(
|
|
3041
|
+
call.toolCallId,
|
|
3042
|
+
new Error(
|
|
3043
|
+
`Pending proxy call '${call.toolName}' (${call.toolCallId}) was orphaned by a new user turn; rejecting`
|
|
3044
|
+
)
|
|
3045
|
+
);
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
2921
3048
|
proc.stdin?.write(userMsg + "\n");
|
|
2922
3049
|
log.debug("sent user message", { textLength: userMsg.length });
|
|
2923
3050
|
};
|