Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
39 changes: 38 additions & 1 deletion packages/provider-opencode/src/server/driver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import * as NodePath from "node:path";
import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { assert, it } from "@effect/vitest";
import { DEFAULT_SERVER_SETTINGS, ProviderInstanceId } from "@t3tools/contracts";
import {
DEFAULT_SERVER_SETTINGS,
ProviderInstanceId,
ProviderSessionId,
ThreadId,
} from "@t3tools/contracts";
import type { OpenCodeSettings } from "../settings.ts";
import * as HostProcess from "@t3tools/shared/HostProcess";
import * as Deferred from "effect/Deferred";
Expand Down Expand Up @@ -575,4 +580,36 @@ it.layer(layerUpdate)("OpenCodeDriver updates", (it) => {
assert.isNull(v2.update);
}).pipe(Effect.scoped),
);

it.effect(
"opens sessions through OpenCode 2 after the binary is upgraded in place from 1.x",
() =>
Effect.gen(function* () {
serverStarts.length = 0;
versionOutput.current = "1.18.18\n";
const instance = yield* create({}, noHttp);
const openSession = instance.orchestrationAdapter
.openSession({
threadId: ThreadId.make("thread-upgraded"),
providerSessionId: ProviderSessionId.make("session-upgraded"),
modelSelection: { instanceId: instance.instanceId, model: "opencode/big-pickle" },
runtimePolicy: { runtimeMode: "full-access", interactionMode: "default", cwd: null },
})
.pipe(Effect.scoped, Effect.flip);

yield* openSession;
assert.deepStrictEqual(serverStarts, ["connect"], "1.x opens through the 1.x adapter");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

versionOutput.current = "opencode v2.2.4\n";
Comment thread
juliusmarminge marked this conversation as resolved.
Resolved
serverStarts.length = 0;
yield* openSession;
// The initial status check may also start a 2.x server here; only the 1.x adapter connects.
assert.notInclude(
serverStarts,
"connect",
"after upgrading to 2.x, the next session must not open through the 1.x adapter",
);
assert.include(serverStarts, "start", "the OpenCode 2 adapter starts its server");
}).pipe(Effect.scoped),
);
});
7 changes: 4 additions & 3 deletions packages/provider-opencode/src/server/versionProbe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ describe("OpenCode version probe", () => {
}),
);

it.effect("remembers a probed runtime but never a failure", () =>
it.effect("remembers a probed 2.x runtime but re-probes 1.x and failures", () =>
Effect.gen(function* () {
const outputs = ["", "opencode v2.0.18\n", "1.18.32\n"];
const outputs = ["", "opencode v2.0.18\n", "1.18.32\n", "1.18.32\n"];
let calls = 0;
const runtime = {
runOpenCodeCommand: () => {
Expand Down Expand Up @@ -190,8 +190,9 @@ describe("OpenCode version probe", () => {
assert.strictEqual((yield* probe.get).generation, "v2");
assert.strictEqual(calls, 2);
assert.strictEqual((yield* probe.refresh).generation, "v1");
assert.strictEqual((yield* probe.get).generation, "v1");
assert.strictEqual(calls, 3);
assert.strictEqual((yield* probe.get).generation, "v1");
assert.strictEqual(calls, 4);
}),
);
});
5 changes: 4 additions & 1 deletion packages/provider-opencode/src/server/versionProbe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,14 @@ export const probeOpenCodeRuntime = (
? probeOpenCodeServer(settings.serverUrl, settings.serverPassword)
: probeOpenCodeBinary(settings.binaryPath, environment);

// Only 2.x is kept: 1.x can be upgraded in place while T3 runs, and 2.x converts the shared
// database, so going back is unsupported.
export const makeOpenCodeRuntimeProbe = <E>(probe: Effect.Effect<ProbedOpenCode, E>) =>
Effect.map(
Cache.makeWith(() => probe, {
capacity: 1,
timeToLive: (exit) => (Exit.isSuccess(exit) ? Duration.infinity : Duration.zero),
timeToLive: (exit) =>
Exit.isSuccess(exit) && exit.value.generation === "v2" ? Duration.infinity : Duration.zero,
}),
(cache) => ({
get: Cache.get(cache, undefined),
Expand Down
Loading