Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4150,26 +4150,37 @@ it.layer(McpProviderSessions.layer)("OpenCode2 adapter", (it) => {
promptAccepted,
event("session.execution.succeeded", { sessionID: SESSION }),
];
const prompts: Array<unknown> = [];
// Idle Location eviction drops OpenCode's runtime MCP servers without a reconnect.
const { runtime, thread } = yield* resumed([
add,
reply("mcp.add", null),
...turn,
add,
reply("mcp.add", { status: 500, body: { message: "MCP unavailable" } }),
out("session.instructions.entry.put", {
sessionID: SESSION,
key: "t3-code",
value: buildRuntimeInstructions({ harness: "OpenCode", model: bigPickle.model }),
}),
reply("session.instructions.entry.put", null),
...turn,
out("mcp.remove", { server, "location[directory]": WORK }),
reply("mcp.remove", null),
]);
const { runtime, thread } = yield* resumed(
[
add,
reply("mcp.add", null),
...turn,
add,
reply("mcp.add", { status: 500, body: { message: "MCP unavailable" } }),
out("session.instructions.entry.put", {
sessionID: SESSION,
key: "t3-code",
value: buildRuntimeInstructions({ harness: "OpenCode", model: bigPickle.model }),
}),
reply("session.instructions.entry.put", null),
...turn,
out("mcp.remove", { server, "location[directory]": WORK }),
reply("mcp.remove", null),
],
{ onPrompt: (input) => prompts.push(input) },
);
for (let index = 0; index < 2; index++) {
const terminal = yield* terminalOf(runtime).pipe(Effect.forkScoped);
yield* runtime.startTurn(turnInput(thread));
const started = yield* runtime.startTurn(turnInput(thread)).pipe(Effect.forkScoped);
// OpenCode before 2.2.6 loads a newly added server's tools after a debounce.
if (index === 0) {
yield* TestClock.adjust("249 millis");
assert.lengthOf(prompts, 0, "prompted before OpenCode loaded the new MCP server's tools");
yield* TestClock.adjust("1 millis");
}
yield* Fiber.join(started);
assert.equal((yield* Fiber.join(terminal))?.status, "completed");
}
}).pipe(Effect.scoped),
Expand Down Expand Up @@ -4230,7 +4241,11 @@ it.layer(McpProviderSessions.layer)("OpenCode2 adapter", (it) => {
runtimePolicy: policy(),
});
const terminal = yield* terminalOf(runtime).pipe(Effect.forkScoped);
yield* runtime.startTurn({ ...turnInput(thread), threadId: child });
const started = yield* runtime
.startTurn({ ...turnInput(thread), threadId: child })
.pipe(Effect.forkScoped);
yield* TestClock.adjust("250 millis");
yield* Fiber.join(started);
assert.equal((yield* Fiber.join(terminal))?.status, "completed");
yield* runtime.unloadThread!({ providerThread: thread });
}).pipe(Effect.scoped),
Expand Down
6 changes: 6 additions & 0 deletions packages/provider-opencode/src/server/v2/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ const RECONNECT_WAIT = "30 seconds";
const LOST_BACKGROUND =
"T3 Code lost its connection to OpenCode while this subagent ran, so its result is not shown.";
const INVENTORY_TIMEOUT = "5 seconds";
const MCP_TOOL_RELOAD_SETTLE = "250 millis";
const ACTIVE_CHECK_TIMEOUT = "5 seconds";
// Other failures may still have admitted the prompt.
const CLEAR_PROMPT_REJECTIONS: ReadonlySet<string> = new Set([
Expand Down Expand Up @@ -4060,6 +4061,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr
// Re-adding an unchanged config keeps its existing connection.
let mcpAvailable = false;
if (wanted !== undefined) {
const fresh = state.mcp === undefined;
mcpAvailable = yield* client.mcp
.add({
server: name,
Expand All @@ -4081,6 +4083,10 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr
),
);
if (mcpAvailable) state.mcp = wanted;
// Before 2.2.6, OpenCode loads a new server's tools 100 ms after it connects, and a prompt sent sooner lacks them.
if (mcpAvailable && fresh && compareSemverVersions(connection.version, "2.2.6") < 0) {
yield* Effect.sleep(MCP_TOOL_RELOAD_SETTLE);
}
}
const instructions = [
buildRuntimeInstructions({ harness: "OpenCode", model: turnInput.modelSelection.model }),
Expand Down
Loading