@khalilgharbaoui/opencode-claude-code-plugin 0.53.1 → 0.53.3
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 +1 -1
- package/dist/index.js +176 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +12 -4
package/dist/index.d.ts
CHANGED
|
@@ -1364,7 +1364,7 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
1364
1364
|
private effectiveMcpConfig;
|
|
1365
1365
|
/** Resolve ProxyToolDef[] for the configured proxyTools names. */
|
|
1366
1366
|
private resolvedProxyTools;
|
|
1367
|
-
/** Resolve ProxyToolDef[] for opencode's MCP-backed tools. */
|
|
1367
|
+
/** Resolve ProxyToolDef[] for opencode's MCP-backed tools, and route the rest. */
|
|
1368
1368
|
private resolvedProxyMcpTools;
|
|
1369
1369
|
/** One `client.tool.list()` fetch, shaped for this turn's gates. */
|
|
1370
1370
|
private fetchLiveToolInfo;
|
package/dist/index.js
CHANGED
|
@@ -4044,7 +4044,13 @@ var TASK_INPUT_PROPERTIES = {
|
|
|
4044
4044
|
},
|
|
4045
4045
|
background: {
|
|
4046
4046
|
type: "boolean",
|
|
4047
|
-
|
|
4047
|
+
// Both majors, for the reason the `task_id` field names both: a model
|
|
4048
|
+
// told to look for only V1's envelope finds nothing on opencode 2, where
|
|
4049
|
+
// a background dispatch answers in prose, and then has no task_id to pass
|
|
4050
|
+
// to task_status or task_cancel. This constant is shared by `task` and by
|
|
4051
|
+
// every `task_batch` item and is built with no dialect in hand, so it
|
|
4052
|
+
// names both rather than being rewritten per host.
|
|
4053
|
+
description: 'Run the subagent in the background and return immediately instead of its answer: opencode 1.x answers with a `<task id="..." state="running">` envelope, opencode 2 with `The subagent is working in the background (sessionID: ses_...)`. You are notified automatically when it completes. Do NOT sleep, poll or proactively check on its progress.'
|
|
4048
4054
|
}
|
|
4049
4055
|
};
|
|
4050
4056
|
var TASK_INPUT_PROPERTIES_NO_BACKGROUND = Object.fromEntries(
|
|
@@ -4673,7 +4679,11 @@ function resolveProxyOpencodeToolDefs(options) {
|
|
|
4673
4679
|
return out;
|
|
4674
4680
|
}
|
|
4675
4681
|
function resolveMcpProxyToolDefs(options) {
|
|
4676
|
-
const empty = {
|
|
4682
|
+
const empty = {
|
|
4683
|
+
defs: [],
|
|
4684
|
+
coveredServers: /* @__PURE__ */ new Set(),
|
|
4685
|
+
matchedServers: /* @__PURE__ */ new Set()
|
|
4686
|
+
};
|
|
4677
4687
|
const serverNames = options.serverNames ?? [];
|
|
4678
4688
|
if (serverNames.length === 0) return empty;
|
|
4679
4689
|
const tools = options.tools;
|
|
@@ -4682,6 +4692,7 @@ function resolveMcpProxyToolDefs(options) {
|
|
|
4682
4692
|
const taken = options.taken ?? /* @__PURE__ */ new Set();
|
|
4683
4693
|
const defs = [];
|
|
4684
4694
|
const coveredServers = /* @__PURE__ */ new Set();
|
|
4695
|
+
const matchedServers = /* @__PURE__ */ new Set();
|
|
4685
4696
|
const seen = /* @__PURE__ */ new Set();
|
|
4686
4697
|
const collided = [];
|
|
4687
4698
|
for (const tool of tools) {
|
|
@@ -4693,6 +4704,7 @@ function resolveMcpProxyToolDefs(options) {
|
|
|
4693
4704
|
(server2) => name === server2 || name.startsWith(`${server2}_`)
|
|
4694
4705
|
);
|
|
4695
4706
|
if (!matchedServer) continue;
|
|
4707
|
+
matchedServers.add(matchedServer);
|
|
4696
4708
|
if (seen.has(name)) continue;
|
|
4697
4709
|
if (taken.has(name)) {
|
|
4698
4710
|
collided.push(name);
|
|
@@ -4712,7 +4724,7 @@ function resolveMcpProxyToolDefs(options) {
|
|
|
4712
4724
|
{ collided }
|
|
4713
4725
|
);
|
|
4714
4726
|
}
|
|
4715
|
-
return { defs, coveredServers };
|
|
4727
|
+
return { defs, coveredServers, matchedServers };
|
|
4716
4728
|
}
|
|
4717
4729
|
function readBody(req) {
|
|
4718
4730
|
return new Promise((resolve6, reject) => {
|
|
@@ -5701,7 +5713,7 @@ async function fetchSessionRunState(sessionID, directory) {
|
|
|
5701
5713
|
async function settleSessionRunState(sessionID, options = {}) {
|
|
5702
5714
|
const pollMs = options.pollMs ?? 150;
|
|
5703
5715
|
const windowMs = options.windowMs ?? 1500;
|
|
5704
|
-
const
|
|
5716
|
+
const started2 = Date.now();
|
|
5705
5717
|
let last = "unknown";
|
|
5706
5718
|
let directory = options.directory;
|
|
5707
5719
|
for (; ; ) {
|
|
@@ -5709,7 +5721,7 @@ async function settleSessionRunState(sessionID, options = {}) {
|
|
|
5709
5721
|
const state = await fetchSessionRunState(sessionID, directory ?? "");
|
|
5710
5722
|
if (state === "busy") return "busy";
|
|
5711
5723
|
if (state !== "unknown") last = state;
|
|
5712
|
-
if (Date.now() -
|
|
5724
|
+
if (Date.now() - started2 >= windowMs) return last;
|
|
5713
5725
|
await new Promise((resolve6) => setTimeout(resolve6, pollMs));
|
|
5714
5726
|
}
|
|
5715
5727
|
}
|
|
@@ -5759,9 +5771,9 @@ async function waitForPendingMcpServers(first, budgetMs, signal) {
|
|
|
5759
5771
|
let current = first;
|
|
5760
5772
|
const waitedFor = pendingMcpServers(current);
|
|
5761
5773
|
if (waitedFor.length === 0) return current;
|
|
5762
|
-
const
|
|
5774
|
+
const started2 = Date.now();
|
|
5763
5775
|
for (; ; ) {
|
|
5764
|
-
if (Date.now() -
|
|
5776
|
+
if (Date.now() - started2 >= budgetMs) break;
|
|
5765
5777
|
if (signal?.aborted) break;
|
|
5766
5778
|
await new Promise((resolve6) => setTimeout(resolve6, MCP_CONNECT_POLL_MS));
|
|
5767
5779
|
if (signal?.aborted) break;
|
|
@@ -5775,7 +5787,7 @@ async function waitForPendingMcpServers(first, budgetMs, signal) {
|
|
|
5775
5787
|
waitedFor,
|
|
5776
5788
|
stillPending,
|
|
5777
5789
|
budgetMs,
|
|
5778
|
-
elapsedMs: Date.now() -
|
|
5790
|
+
elapsedMs: Date.now() - started2,
|
|
5779
5791
|
aborted: signal?.aborted === true
|
|
5780
5792
|
});
|
|
5781
5793
|
return current;
|
|
@@ -5903,6 +5915,56 @@ function clearBackgroundTasks(sessionKey2) {
|
|
|
5903
5915
|
collected.delete(sessionKey2);
|
|
5904
5916
|
cancelled.delete(sessionKey2);
|
|
5905
5917
|
}
|
|
5918
|
+
var MAX_STARTED = 128;
|
|
5919
|
+
var started = /* @__PURE__ */ new Map();
|
|
5920
|
+
function backgroundTaskIdsIn(text) {
|
|
5921
|
+
const ids = /* @__PURE__ */ new Set();
|
|
5922
|
+
for (const match of text.matchAll(/<task id="([^"\s]+)" state="running">/g)) {
|
|
5923
|
+
ids.add(match[1]);
|
|
5924
|
+
}
|
|
5925
|
+
for (const match of text.matchAll(/in the background \(sessionID: ([^)\s]+)\)/g)) {
|
|
5926
|
+
ids.add(match[1]);
|
|
5927
|
+
}
|
|
5928
|
+
return [...ids];
|
|
5929
|
+
}
|
|
5930
|
+
function noteBackgroundDispatchResult(sessionKey2, result, now = Date.now()) {
|
|
5931
|
+
if (result.kind !== "text" || result.isError === true) return;
|
|
5932
|
+
for (const taskId of backgroundTaskIdsIn(result.text)) {
|
|
5933
|
+
started.delete(taskId);
|
|
5934
|
+
started.set(taskId, { sessionKey: sessionKey2, at: now });
|
|
5935
|
+
while (started.size > MAX_STARTED) {
|
|
5936
|
+
const oldest = started.keys().next();
|
|
5937
|
+
if (oldest.done) break;
|
|
5938
|
+
started.delete(oldest.value);
|
|
5939
|
+
}
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5942
|
+
async function countRunningBackgroundTasks() {
|
|
5943
|
+
const entries = [...started.entries()];
|
|
5944
|
+
const cancelledIds = /* @__PURE__ */ new Set();
|
|
5945
|
+
for (const ids of cancelled.values()) for (const id of ids) cancelledIds.add(id);
|
|
5946
|
+
const states = await Promise.all(
|
|
5947
|
+
entries.map(async ([taskId]) => {
|
|
5948
|
+
if (cancelledIds.has(taskId)) return "stopped";
|
|
5949
|
+
try {
|
|
5950
|
+
const runState = await fetchSessionRunState(taskId);
|
|
5951
|
+
if (runState !== "unknown") {
|
|
5952
|
+
return isBackgroundTaskRunning(runState, []) ? "running" : "stopped";
|
|
5953
|
+
}
|
|
5954
|
+
const replies = await fetchSessionReplies(taskId);
|
|
5955
|
+
if (replies === void 0) return "unreadable";
|
|
5956
|
+
return isBackgroundTaskRunning(runState, replies) ? "running" : "stopped";
|
|
5957
|
+
} catch {
|
|
5958
|
+
return "unreadable";
|
|
5959
|
+
}
|
|
5960
|
+
})
|
|
5961
|
+
);
|
|
5962
|
+
return {
|
|
5963
|
+
running: states.filter((state) => state === "running").length,
|
|
5964
|
+
started: entries.length,
|
|
5965
|
+
unreadable: states.filter((state) => state === "unreadable").length
|
|
5966
|
+
};
|
|
5967
|
+
}
|
|
5906
5968
|
var lastGate;
|
|
5907
5969
|
function recordBackgroundSubagentGate(gate, now = Date.now()) {
|
|
5908
5970
|
lastGate = { ...gate, at: now };
|
|
@@ -7629,21 +7691,21 @@ async function sessionStatus(client, sessionID) {
|
|
|
7629
7691
|
async function waitForSessionIdle(client, sessionID, options = {}) {
|
|
7630
7692
|
const pollMs = options.pollMs ?? IDLE_POLL_MS;
|
|
7631
7693
|
const timeoutMs = options.timeoutMs ?? IDLE_WAIT_MAX_MS;
|
|
7632
|
-
const
|
|
7694
|
+
const started2 = Date.now();
|
|
7633
7695
|
for (; ; ) {
|
|
7634
7696
|
if (options.stop?.()) return true;
|
|
7635
7697
|
if (await sessionStatus(client, sessionID) !== "busy") return true;
|
|
7636
|
-
if (Date.now() -
|
|
7698
|
+
if (Date.now() - started2 >= timeoutMs) return false;
|
|
7637
7699
|
await new Promise((resolve6) => setTimeout(resolve6, pollMs));
|
|
7638
7700
|
}
|
|
7639
7701
|
}
|
|
7640
7702
|
async function deliverAsideInline(client, sessionID, text, options = {}) {
|
|
7641
7703
|
const pollMs = options.inlinePollMs ?? INLINE_POLL_MS;
|
|
7642
7704
|
const timeoutMs = options.inlineWaitMs ?? INLINE_WAIT_MAX_MS;
|
|
7643
|
-
const
|
|
7705
|
+
const started2 = Date.now();
|
|
7644
7706
|
for (; ; ) {
|
|
7645
7707
|
if (emitAsideInline(sessionID, text)) return true;
|
|
7646
|
-
if (Date.now() -
|
|
7708
|
+
if (Date.now() - started2 >= timeoutMs) return false;
|
|
7647
7709
|
if (await sessionStatus(client, sessionID) !== "busy") return false;
|
|
7648
7710
|
await new Promise((resolve6) => setTimeout(resolve6, pollMs));
|
|
7649
7711
|
}
|
|
@@ -7652,12 +7714,12 @@ async function waitForAsideProcess(client, sessionID, options = {}) {
|
|
|
7652
7714
|
const pollMs = options.pollMs ?? IDLE_POLL_MS;
|
|
7653
7715
|
const settleMs = options.settleMs ?? BUSY_SETTLE_MS;
|
|
7654
7716
|
const spawnWaitMs = options.spawnWaitMs ?? SPAWN_WAIT_MAX_MS;
|
|
7655
|
-
const
|
|
7717
|
+
const started2 = Date.now();
|
|
7656
7718
|
for (; ; ) {
|
|
7657
7719
|
const active = findActiveProcessBySessionId(sessionID);
|
|
7658
7720
|
if (active) return active;
|
|
7659
7721
|
const busy = await sessionStatus(client, sessionID) === "busy";
|
|
7660
|
-
const waitedMs = Date.now() -
|
|
7722
|
+
const waitedMs = Date.now() - started2;
|
|
7661
7723
|
if (!busy && waitedMs >= settleMs) {
|
|
7662
7724
|
log.info("btw: no live claude process for session, leaving it to the turn", { sessionID, waitedMs });
|
|
7663
7725
|
return void 0;
|
|
@@ -7672,12 +7734,12 @@ async function waitForAsideProcess(client, sessionID, options = {}) {
|
|
|
7672
7734
|
async function settleSessionBusy(client, sessionID, active, options = {}) {
|
|
7673
7735
|
const pollMs = options.pollMs ?? IDLE_POLL_MS;
|
|
7674
7736
|
const settleMs = options.settleMs ?? BUSY_SETTLE_MS;
|
|
7675
|
-
const
|
|
7737
|
+
const started2 = Date.now();
|
|
7676
7738
|
for (; ; ) {
|
|
7677
7739
|
const status = await sessionStatus(client, sessionID);
|
|
7678
7740
|
if (status === "busy") return true;
|
|
7679
7741
|
if (status === "unknown") return isProcessBusy(active);
|
|
7680
|
-
if (Date.now() -
|
|
7742
|
+
if (Date.now() - started2 >= settleMs) return false;
|
|
7681
7743
|
await new Promise((resolve6) => setTimeout(resolve6, pollMs));
|
|
7682
7744
|
}
|
|
7683
7745
|
}
|
|
@@ -7781,7 +7843,7 @@ async function handleBtwCommand(client, input, options = {}) {
|
|
|
7781
7843
|
);
|
|
7782
7844
|
}
|
|
7783
7845
|
if (!busy) return;
|
|
7784
|
-
const
|
|
7846
|
+
const started2 = Date.now();
|
|
7785
7847
|
const outcome = await Promise.race([
|
|
7786
7848
|
inlineDelivered,
|
|
7787
7849
|
waitForSessionIdle(client, input.sessionID, { ...options, stop: () => inlineDone }).then(
|
|
@@ -7791,14 +7853,14 @@ async function handleBtwCommand(client, input, options = {}) {
|
|
|
7791
7853
|
if (outcome === "inline") {
|
|
7792
7854
|
log.info("btw: answered inside the running turn, dropping the /btw message", {
|
|
7793
7855
|
sessionID: input.sessionID,
|
|
7794
|
-
waitedMs: Date.now() -
|
|
7856
|
+
waitedMs: Date.now() - started2
|
|
7795
7857
|
});
|
|
7796
7858
|
throw new BtwHandledError(BTW_INLINE_HANDLED_MESSAGE);
|
|
7797
7859
|
}
|
|
7798
7860
|
log.info("btw: turn over, releasing the /btw message", {
|
|
7799
7861
|
sessionID: input.sessionID,
|
|
7800
7862
|
idle: outcome === "idle",
|
|
7801
|
-
waitedMs: Date.now() -
|
|
7863
|
+
waitedMs: Date.now() - started2
|
|
7802
7864
|
});
|
|
7803
7865
|
if (outcome === "timeout") {
|
|
7804
7866
|
showToast(client, { title: "btw", message: BTW_TURN_TOO_LONG_MESSAGE, variant: "warning", duration: 8e3 });
|
|
@@ -7927,6 +7989,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
7927
7989
|
"proxy-mcp interceptor failed",
|
|
7928
7990
|
"proxy-mcp rejected a request",
|
|
7929
7991
|
"proxy-mcp tool call timed out",
|
|
7992
|
+
"proxyOpencodeMcpTools cannot route V2 Code Mode's MCP tools, which reach the model through execute; for Code Mode, explicitly allowlist execute in proxyOpencodeTools and use bridgeOpencodeMcp: false with strictMcpConfig: true",
|
|
7930
7993
|
"proxyOpencodeTools entry dropped: a proxy tool already holds that name, and it keeps it",
|
|
7931
7994
|
"proxyOpencodeTools is set but opencode's tool registry did not answer; forwarding nothing this spawn",
|
|
7932
7995
|
"refusing the pid-named scratch directory",
|
|
@@ -9162,6 +9225,10 @@ function formatDoctorReport(report) {
|
|
|
9162
9225
|
}
|
|
9163
9226
|
function formatBackgroundSubagents(state) {
|
|
9164
9227
|
const lines = [];
|
|
9228
|
+
if (state.running) {
|
|
9229
|
+
lines.push(...formatRunningCount(state.running));
|
|
9230
|
+
lines.push("");
|
|
9231
|
+
}
|
|
9165
9232
|
const gate = state.gate;
|
|
9166
9233
|
if (!gate) {
|
|
9167
9234
|
lines.push(
|
|
@@ -9207,6 +9274,19 @@ function formatBackgroundSubagents(state) {
|
|
|
9207
9274
|
}
|
|
9208
9275
|
return lines;
|
|
9209
9276
|
}
|
|
9277
|
+
function formatRunningCount(count) {
|
|
9278
|
+
if (count.started === 0) {
|
|
9279
|
+
return ["running now: 0 (none started by this process)"];
|
|
9280
|
+
}
|
|
9281
|
+
const lines = [`running now: ${count.running} (of ${count.started} started by this process)`];
|
|
9282
|
+
if (count.unreadable > 0) {
|
|
9283
|
+
lines.push("");
|
|
9284
|
+
lines.push(
|
|
9285
|
+
`${count.unreadable} could not be read from opencode (deleted, or no session route on this build) and are not counted as running.`
|
|
9286
|
+
);
|
|
9287
|
+
}
|
|
9288
|
+
return lines;
|
|
9289
|
+
}
|
|
9210
9290
|
function describeBackgroundGateSource(gate) {
|
|
9211
9291
|
if (gate.hostApi === "v2") {
|
|
9212
9292
|
return "opencode 2 offers `background` unconditionally, so the registry is not consulted";
|
|
@@ -9291,7 +9371,8 @@ async function gatherDoctorReport(options) {
|
|
|
9291
9371
|
planUsage,
|
|
9292
9372
|
backgroundSubagents: {
|
|
9293
9373
|
gate: snapshotBackgroundSubagentGate(),
|
|
9294
|
-
ledgers: snapshotBackgroundTasks()
|
|
9374
|
+
ledgers: snapshotBackgroundTasks(),
|
|
9375
|
+
running: await countRunningBackgroundTasks().catch(() => void 0)
|
|
9295
9376
|
}
|
|
9296
9377
|
};
|
|
9297
9378
|
}
|
|
@@ -11962,12 +12043,12 @@ function spawnInteractiveProcess(opts) {
|
|
|
11962
12043
|
}
|
|
11963
12044
|
}
|
|
11964
12045
|
closeProxyServer();
|
|
11965
|
-
const
|
|
12046
|
+
const started2 = startPromise !== null;
|
|
11966
12047
|
try {
|
|
11967
12048
|
session.dispose();
|
|
11968
12049
|
} catch {
|
|
11969
12050
|
}
|
|
11970
|
-
if (!
|
|
12051
|
+
if (!started2 && proc.exitCode === null && proc.signalCode === null) {
|
|
11971
12052
|
proc.signalCode = "SIGTERM";
|
|
11972
12053
|
proc.emit("exit", null, "SIGTERM");
|
|
11973
12054
|
proc.emit("close", null, "SIGTERM");
|
|
@@ -12120,6 +12201,17 @@ This opencode host runs background subagents, so \`mcp__opencode_proxy__task\` t
|
|
|
12120
12201
|
- After starting one, keep working on something that does not overlap it, then end your turn normally. When the subagent finishes, opencode delivers its result into this conversation as a new message on its own. Do not sleep, poll, or ask the operator to wait.
|
|
12121
12202
|
- The \`id\` in that envelope is the task_id. \`mcp__opencode_proxy__task_status\` reads a result back if the automatic delivery did not arrive (an interrupted or errored turn); \`mcp__opencode_proxy__task_cancel\` stops a background subagent you no longer want. Neither is a progress poll.
|
|
12122
12203
|
- Foreground is still the default and still the right choice when you need the answer before you can continue. Use background only for work that genuinely runs alongside yours.`;
|
|
12204
|
+
var BACKGROUND_SUBAGENT_HINT_V2 = `## Background subagents
|
|
12205
|
+
|
|
12206
|
+
This opencode host runs background subagents, so \`mcp__opencode_proxy__task\` takes \`background: true\`.
|
|
12207
|
+
|
|
12208
|
+
- A background dispatch returns at once with \`The subagent is working in the background (sessionID: ses_...)\` instead of the subagent's answer. That is success, not a truncated result.
|
|
12209
|
+
- After starting one, keep working on something that does not overlap it, then end your turn normally. When the subagent finishes, opencode delivers its result into this conversation on its own, as a \`<subagent sessionID="..." state="completed">\` message. Do not sleep, poll, or ask the operator to wait.
|
|
12210
|
+
- That \`sessionID\` is the task_id. \`mcp__opencode_proxy__task_status\` reads a result back if the automatic delivery did not arrive (an interrupted or errored turn); \`mcp__opencode_proxy__task_cancel\` stops a background subagent you no longer want. Neither is a progress poll.
|
|
12211
|
+
- Foreground is still the default and still the right choice when you need the answer before you can continue. Use background only for work that genuinely runs alongside yours.`;
|
|
12212
|
+
function backgroundSubagentHint(dialect) {
|
|
12213
|
+
return dialect === "v2" ? BACKGROUND_SUBAGENT_HINT_V2 : BACKGROUND_SUBAGENT_HINT;
|
|
12214
|
+
}
|
|
12123
12215
|
var QUESTION_PROXY_HINT = `## Asking the operator questions
|
|
12124
12216
|
|
|
12125
12217
|
Structured questions in this environment go through exactly one tool: \`mcp__opencode_proxy__question\`.
|
|
@@ -12720,7 +12812,13 @@ function resolvedProxyTools(config) {
|
|
|
12720
12812
|
}
|
|
12721
12813
|
return picked.length > 0 ? picked : null;
|
|
12722
12814
|
}
|
|
12723
|
-
function
|
|
12815
|
+
function decideMcpServerRoute(input) {
|
|
12816
|
+
if (input.covered) return "proxy";
|
|
12817
|
+
if (input.inToolSet) return "bridge";
|
|
12818
|
+
if (input.toolSetKnown && input.status === "connected") return "withhold";
|
|
12819
|
+
return "bridge";
|
|
12820
|
+
}
|
|
12821
|
+
function resolvedProxyMcpTools(config, allEnabledServerNames, modelTools, taken, runtimeStatus) {
|
|
12724
12822
|
if (config.proxyOpencodeMcpTools !== true) return null;
|
|
12725
12823
|
if (config.bridgeOpencodeMcp === false) return null;
|
|
12726
12824
|
if (allEnabledServerNames.length === 0) return null;
|
|
@@ -12729,18 +12827,47 @@ function resolvedProxyMcpTools(config, allEnabledServerNames, modelTools, taken)
|
|
|
12729
12827
|
tools: modelTools,
|
|
12730
12828
|
taken
|
|
12731
12829
|
});
|
|
12732
|
-
|
|
12830
|
+
const codeMode = config.hostApi === "v2" && (modelTools?.some((tool) => tool.name === "execute") ?? false);
|
|
12831
|
+
const toolSetKnown = (modelTools?.length ?? 0) > 0 && !codeMode;
|
|
12832
|
+
const withheld = [];
|
|
12833
|
+
const bridged = [];
|
|
12834
|
+
for (const server2 of allEnabledServerNames) {
|
|
12835
|
+
const route = decideMcpServerRoute({
|
|
12836
|
+
covered: resolution.coveredServers.has(server2),
|
|
12837
|
+
inToolSet: resolution.matchedServers.has(server2),
|
|
12838
|
+
status: runtimeStatus?.[server2],
|
|
12839
|
+
toolSetKnown
|
|
12840
|
+
});
|
|
12841
|
+
if (route === "withhold") withheld.push(server2);
|
|
12842
|
+
else if (route === "bridge") bridged.push(server2);
|
|
12843
|
+
}
|
|
12844
|
+
if (codeMode && resolution.defs.length === 0) {
|
|
12733
12845
|
log.warn(
|
|
12734
|
-
"proxyOpencodeMcpTools
|
|
12735
|
-
{ servers: allEnabledServerNames
|
|
12846
|
+
"proxyOpencodeMcpTools cannot route V2 Code Mode's MCP tools, which reach the model through execute; for Code Mode, explicitly allowlist execute in proxyOpencodeTools and use bridgeOpencodeMcp: false with strictMcpConfig: true",
|
|
12847
|
+
{ servers: allEnabledServerNames }
|
|
12736
12848
|
);
|
|
12737
|
-
return null;
|
|
12738
12849
|
}
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
|
|
12850
|
+
if (withheld.length > 0 || bridged.length > 0) {
|
|
12851
|
+
log.info("proxyOpencodeMcpTools: MCP servers outside this agent's tool set", {
|
|
12852
|
+
proxied: [...resolution.coveredServers],
|
|
12853
|
+
withheld,
|
|
12854
|
+
bridged,
|
|
12855
|
+
modelTools: modelTools?.length ?? 0,
|
|
12856
|
+
statusKnown: runtimeStatus !== void 0
|
|
12857
|
+
});
|
|
12858
|
+
} else {
|
|
12859
|
+
log.debug("routing opencode MCP tools through the proxy", {
|
|
12860
|
+
servers: [...resolution.coveredServers],
|
|
12861
|
+
tools: resolution.defs.map((def) => def.name)
|
|
12862
|
+
});
|
|
12863
|
+
}
|
|
12864
|
+
const exclude = /* @__PURE__ */ new Set([...resolution.coveredServers, ...withheld]);
|
|
12865
|
+
return {
|
|
12866
|
+
resolution,
|
|
12867
|
+
withheld,
|
|
12868
|
+
bridged,
|
|
12869
|
+
excludeServers: exclude.size > 0 ? exclude : void 0
|
|
12870
|
+
};
|
|
12744
12871
|
}
|
|
12745
12872
|
async function fetchLiveToolInfo(config, modelId) {
|
|
12746
12873
|
const items = await fetchOpencodeToolList(
|
|
@@ -14667,13 +14794,14 @@ var ClaudeCodeLanguageModel = class {
|
|
|
14667
14794
|
resolvedProxyTools() {
|
|
14668
14795
|
return resolvedProxyTools(this.config);
|
|
14669
14796
|
}
|
|
14670
|
-
/** Resolve ProxyToolDef[] for opencode's MCP-backed tools. */
|
|
14671
|
-
resolvedProxyMcpTools(allEnabledServerNames, modelTools, taken) {
|
|
14797
|
+
/** Resolve ProxyToolDef[] for opencode's MCP-backed tools, and route the rest. */
|
|
14798
|
+
resolvedProxyMcpTools(allEnabledServerNames, modelTools, taken, runtimeStatus) {
|
|
14672
14799
|
return resolvedProxyMcpTools(
|
|
14673
14800
|
this.config,
|
|
14674
14801
|
allEnabledServerNames,
|
|
14675
14802
|
modelTools,
|
|
14676
|
-
taken
|
|
14803
|
+
taken,
|
|
14804
|
+
runtimeStatus
|
|
14677
14805
|
);
|
|
14678
14806
|
}
|
|
14679
14807
|
/** One `client.tool.list()` fetch, shaped for this turn's gates. */
|
|
@@ -15982,13 +16110,14 @@ var ClaudeCodeLanguageModel = class {
|
|
|
15982
16110
|
void 0,
|
|
15983
16111
|
runtimeStatus
|
|
15984
16112
|
);
|
|
15985
|
-
const
|
|
16113
|
+
const mcpRouting = self.resolvedProxyMcpTools(
|
|
15986
16114
|
discovery.allEnabledServerNames,
|
|
15987
16115
|
options.tools,
|
|
15988
|
-
new Set((resolvedProxy ?? []).map((def) => def.name))
|
|
16116
|
+
new Set((resolvedProxy ?? []).map((def) => def.name)),
|
|
16117
|
+
runtimeStatus
|
|
15989
16118
|
);
|
|
15990
|
-
const proxyMcpTools =
|
|
15991
|
-
const excludeServers =
|
|
16119
|
+
const proxyMcpTools = mcpRouting && mcpRouting.resolution.defs.length > 0 ? mcpRouting.resolution.defs : null;
|
|
16120
|
+
const excludeServers = mcpRouting?.excludeServers;
|
|
15992
16121
|
const taskProxyEnabled = resolvedProxy?.some((t) => t.name === "task") ?? false;
|
|
15993
16122
|
const questionProxyEnabled = resolvedProxy?.some((t) => t.name === "question") ?? false;
|
|
15994
16123
|
const opencodeToolsRequested = (self.config.proxyOpencodeTools?.length ?? 0) > 0;
|
|
@@ -16005,6 +16134,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
16005
16134
|
};
|
|
16006
16135
|
let enrichedProxy = resolvedProxy;
|
|
16007
16136
|
let backgroundSubagentsSupported = false;
|
|
16137
|
+
const hostDialect = self.config.hostApi === "v2" ? "v2" : "v1";
|
|
16008
16138
|
if (enrichedProxy && taskProxyEnabled) {
|
|
16009
16139
|
enrichedProxy = overlayTaskProxyDescription(
|
|
16010
16140
|
enrichedProxy,
|
|
@@ -16019,7 +16149,6 @@ var ClaudeCodeLanguageModel = class {
|
|
|
16019
16149
|
)
|
|
16020
16150
|
)
|
|
16021
16151
|
});
|
|
16022
|
-
const hostDialect = self.config.hostApi === "v2" ? "v2" : "v1";
|
|
16023
16152
|
backgroundSubagentsSupported = liveTaskSupportsBackground(
|
|
16024
16153
|
liveToolInfo.taskParameters,
|
|
16025
16154
|
hostDialect
|
|
@@ -16096,6 +16225,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
16096
16225
|
excludeServers,
|
|
16097
16226
|
taskProxyEnabled,
|
|
16098
16227
|
backgroundSubagentsSupported,
|
|
16228
|
+
hostDialect,
|
|
16099
16229
|
questionProxyActive,
|
|
16100
16230
|
opencodeToolDefs,
|
|
16101
16231
|
pluginCompressEnabled,
|
|
@@ -16161,7 +16291,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
16161
16291
|
// dispatch instead of calling the proxy (h #g74).
|
|
16162
16292
|
[
|
|
16163
16293
|
...wiring.taskProxyEnabled ? [SUBAGENT_DISPATCH_HINT] : [],
|
|
16164
|
-
...wiring.backgroundSubagentsSupported ? [
|
|
16294
|
+
...wiring.backgroundSubagentsSupported ? [backgroundSubagentHint(wiring.hostDialect)] : [],
|
|
16165
16295
|
...wiring.questionProxyActive ? [QUESTION_PROXY_HINT] : []
|
|
16166
16296
|
],
|
|
16167
16297
|
{
|
|
@@ -16275,6 +16405,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
16275
16405
|
excludeServers,
|
|
16276
16406
|
taskProxyEnabled,
|
|
16277
16407
|
backgroundSubagentsSupported,
|
|
16408
|
+
hostDialect,
|
|
16278
16409
|
questionProxyActive,
|
|
16279
16410
|
opencodeToolDefs,
|
|
16280
16411
|
pluginCompressEnabled,
|
|
@@ -16292,7 +16423,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
16292
16423
|
[
|
|
16293
16424
|
...extractSystemMessages(options.prompt),
|
|
16294
16425
|
...taskProxyEnabled ? [SUBAGENT_DISPATCH_HINT] : [],
|
|
16295
|
-
...backgroundSubagentsSupported ? [
|
|
16426
|
+
...backgroundSubagentsSupported ? [backgroundSubagentHint(hostDialect)] : [],
|
|
16296
16427
|
...questionProxyActive ? [QUESTION_PROXY_HINT] : [],
|
|
16297
16428
|
...self.config.hostApi === "v2" && options.tools?.some((t) => t.name === "execute") ? [codeModeProxyHint(opencodeToolDefs.some((t) => t.name === "execute"))] : []
|
|
16298
16429
|
],
|
|
@@ -16842,6 +16973,9 @@ var ClaudeCodeLanguageModel = class {
|
|
|
16842
16973
|
recoveryRequired: channelClosed || state.unattendedTurnEnded
|
|
16843
16974
|
});
|
|
16844
16975
|
}
|
|
16976
|
+
if (call.toolName === "task" || call.toolName === TASK_BATCH_TOOL_NAME) {
|
|
16977
|
+
noteBackgroundDispatchResult(sk, result);
|
|
16978
|
+
}
|
|
16845
16979
|
resolvePendingProxyCallById(call.toolCallId, result);
|
|
16846
16980
|
} else {
|
|
16847
16981
|
log.info(
|