Skip to content
Draft
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
2 changes: 2 additions & 0 deletions apps/server/src/provider/Drivers/ClaudeDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
normalizeCommandPath,
resolveProviderMaintenanceCapabilitiesEffect,
} from "../providerMaintenance.ts";
import { enrichProviderSnapshotWithCompatibilityAdvisory } from "../providerCompatibility.ts";
import { makeClaudeCapabilitiesCacheKey, makeClaudeContinuationGroupKey } from "./ClaudeHome.ts";
const decodeClaudeSettings = Schema.decodeSync(ClaudeSettings);

Expand Down Expand Up @@ -171,6 +172,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {
checkProvider,
enrichSnapshot: ({ snapshot, publishSnapshot }) =>
enrichProviderSnapshotWithVersionAdvisory(snapshot, maintenanceCapabilities).pipe(
Effect.flatMap(enrichProviderSnapshotWithCompatibilityAdvisory),
Effect.provideService(HttpClient.HttpClient, httpClient),
Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
),
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/provider/Drivers/CodexDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
makePackageManagedProviderMaintenanceResolver,
resolveProviderMaintenanceCapabilitiesEffect,
} from "../providerMaintenance.ts";
import { enrichProviderSnapshotWithCompatibilityAdvisory } from "../providerCompatibility.ts";
import {
codexContinuationIdentity,
materializeCodexShadowHome,
Expand Down Expand Up @@ -171,6 +172,7 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = {
checkProvider,
enrichSnapshot: ({ snapshot, publishSnapshot }) =>
enrichProviderSnapshotWithVersionAdvisory(snapshot, maintenanceCapabilities).pipe(
Effect.flatMap(enrichProviderSnapshotWithCompatibilityAdvisory),
Effect.provideService(HttpClient.HttpClient, httpClient),
Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
),
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/provider/Drivers/OpenCodeDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
normalizeCommandPath,
resolveProviderMaintenanceCapabilitiesEffect,
} from "../providerMaintenance.ts";
import { enrichProviderSnapshotWithCompatibilityAdvisory } from "../providerCompatibility.ts";
const decodeOpenCodeSettings = Schema.decodeSync(OpenCodeSettings);

const DRIVER_KIND = ProviderDriverKind.make("opencode");
Expand Down Expand Up @@ -150,6 +151,7 @@ export const OpenCodeDriver: ProviderDriver<OpenCodeSettings, OpenCodeDriverEnv>
checkProvider,
enrichSnapshot: ({ snapshot, publishSnapshot }) =>
enrichProviderSnapshotWithVersionAdvisory(snapshot, maintenanceCapabilities).pipe(
Effect.flatMap(enrichProviderSnapshotWithCompatibilityAdvisory),
Effect.provideService(HttpClient.HttpClient, httpClient),
Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
),
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/provider/Layers/CursorProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
enrichProviderSnapshotWithVersionAdvisory,
type ProviderMaintenanceCapabilities,
} from "../providerMaintenance.ts";
import { enrichProviderSnapshotWithCompatibilityAdvisory } from "../providerCompatibility.ts";
import { AcpSessionRuntime } from "../acp/AcpSessionRuntime.ts";

const PROVIDER = ProviderDriverKind.make("cursor");
Expand Down Expand Up @@ -1242,6 +1243,7 @@ export const enrichCursorSnapshot = (input: {
snapshot,
input.maintenanceCapabilities,
).pipe(
Effect.flatMap(enrichProviderSnapshotWithCompatibilityAdvisory),
Effect.provideService(HttpClient.HttpClient, input.httpClient),
Effect.flatMap((enrichedSnapshot) =>
publishSnapshot(stampIdentity(enrichedSnapshot)).pipe(Effect.as(enrichedSnapshot)),
Expand Down
124 changes: 124 additions & 0 deletions apps/server/src/provider/providerCompatibility.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { afterEach, describe, expect, it } from "vitest";
import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts";
import * as Effect from "effect/Effect";
import { HttpClient, HttpClientResponse } from "effect/unstable/http";

import {
clearProviderCompatibilityCacheForTests,
createProviderCompatibilityAdvisory,
enrichProviderSnapshotWithCompatibilityAdvisory,
type ProviderCompatibilityDocument,
} from "./providerCompatibility.ts";

const codexDriver = ProviderDriverKind.make("codex");

const baseProvider: ServerProvider = {
instanceId: ProviderInstanceId.make("codex"),
driver: codexDriver,
displayName: "Codex",
enabled: true,
installed: true,
version: "0.130.0",
status: "ready",
auth: { status: "authenticated" },
checkedAt: "2026-04-10T00:00:00.000Z",
models: [],
slashCommands: [],
skills: [],
};

function jsonHttpClient(payload: unknown, status = 200): HttpClient.HttpClient {
return HttpClient.make((request) =>
Effect.succeed(
HttpClientResponse.fromWeb(
request,
new Response(JSON.stringify(payload), {
status,
headers: { "content-type": "application/json" },
}),
),
),
);
}

afterEach(() => {
clearProviderCompatibilityCacheForTests();
});

describe("provider compatibility", () => {
it("selects policies by T3 Code version range", () => {
const document: ProviderCompatibilityDocument = {
version: 1,
policies: [
{
t3CodeRange: "<0.1.0",
driver: codexDriver,
recommendedRange: "<0.130.0",
recommendedVersion: "0.129.0",
ranges: [{ status: "broken", range: ">=0.130.0" }],
},
{
t3CodeRange: ">=0.1.0",
driver: codexDriver,
recommendedRange: ">=0.130.0",
recommendedVersion: "0.130.0",
ranges: [{ status: "supported", range: ">=0.130.0" }],
},
],
};

expect(
createProviderCompatibilityAdvisory({
driver: codexDriver,
currentVersion: "0.130.0",
document,
t3CodeVersion: "0.0.22",
}),
).toMatchObject({
status: "broken",
recommendedVersion: "0.129.0",
});
});

it("enriches snapshots from the remote compatibility map when available", async () => {
const remoteDocument = {
version: 1,
policies: [
{
t3CodeRange: ">=0.0.0",
driver: "codex",
recommendedRange: "<0.130.0",
recommendedVersion: "0.129.0",
ranges: [{ status: "broken", range: ">=0.130.0" }],
},
],
};

const enriched = await Effect.runPromise(
enrichProviderSnapshotWithCompatibilityAdvisory(baseProvider).pipe(
Effect.provideService(HttpClient.HttpClient, jsonHttpClient(remoteDocument)),
),
);

expect(enriched.status).toBe("error");
expect(enriched.compatibilityAdvisory).toMatchObject({
status: "broken",
recommendedVersion: "0.129.0",
});
});

it("falls back to the bundled map when the remote compatibility fetch fails", async () => {
const enriched = await Effect.runPromise(
enrichProviderSnapshotWithCompatibilityAdvisory({
...baseProvider,
version: "0.128.0",
}).pipe(Effect.provideService(HttpClient.HttpClient, jsonHttpClient({}, 404))),
);

expect(enriched.status).toBe("error");
expect(enriched.compatibilityAdvisory).toMatchObject({
status: "broken",
recommendedVersion: "0.129.0",
});
});
});
Loading
Loading