refactor(core): move database schema ownership (#29068)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Dax
2026-05-30 21:08:38 -04:00
committed by GitHub
co-authored by copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
parent 6bcb9cb9bb
commit 7f571d36ea
390 changed files with 11127 additions and 9164 deletions
+38 -40
View File
@@ -1,4 +1,5 @@
import { describe, expect, test } from "bun:test"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import type { NamedError } from "@opencode-ai/core/util/error"
import { APICallError } from "ai"
import { setTimeout as sleep } from "node:timers/promises"
@@ -6,20 +7,19 @@ import { Effect, Layer, Schedule, Schema } from "effect"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { SessionRetry } from "../../src/session/retry"
import { MessageV2 } from "../../src/session/message-v2"
import { ProviderID } from "../../src/provider/schema"
import { ProviderError } from "../../src/provider/error"
import { SessionID } from "../../src/session/schema"
import { SessionStatus } from "../../src/session/status"
import { provideTmpdirInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
import { ProviderV2 } from "@opencode-ai/core/provider"
const providerID = ProviderID.make("test")
const providerID = ProviderV2.ID.make("test")
const retryProvider = "test"
const it = testEffect(Layer.mergeAll(SessionStatus.defaultLayer, CrossSpawnSpawner.defaultLayer))
function apiError(headers?: Record<string, string>): MessageV2.APIError {
return Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
function apiError(headers?: Record<string, string>): SessionLegacy.APIError {
return Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "boom",
isRetryable: true,
responseHeaders: headers,
@@ -85,9 +85,8 @@ describe("session.retry.delay", () => {
expect(SessionRetry.delay(1, error)).toBe(SessionRetry.RETRY_MAX_DELAY)
})
it.live("policy updates retry status and increments attempts", () =>
provideTmpdirInstance(() =>
Effect.gen(function* () {
it.instance("policy updates retry status and increments attempts", () =>
Effect.gen(function* () {
const sessionID = SessionID.make("session-retry-test")
const error = apiError({ "retry-after-ms": "0" })
const status = yield* SessionStatus.Service
@@ -95,7 +94,7 @@ describe("session.retry.delay", () => {
const step = yield* Schedule.toStepWithMetadata(
SessionRetry.policy({
provider: "test",
parse: Schema.decodeUnknownSync(MessageV2.APIError.Schema),
parse: Schema.decodeUnknownSync(SessionLegacy.APIError.Schema),
set: (info) =>
status.set(sessionID, {
type: "retry",
@@ -113,8 +112,7 @@ describe("session.retry.delay", () => {
attempt: 2,
message: "boom",
})
}),
),
}),
)
})
@@ -166,7 +164,7 @@ describe("session.retry.retryable", () => {
test("retries transport timeout errors", () => {
const request = MessageV2.fromError(new ProviderError.HeaderTimeoutError(10000), { providerID })
expect(MessageV2.APIError.isInstance(request)).toBe(true)
expect(SessionLegacy.APIError.isInstance(request)).toBe(true)
expect(SessionRetry.retryable(request, retryProvider)).toEqual({
message: "Provider response headers timed out after 10000ms",
})
@@ -177,14 +175,14 @@ describe("session.retry.retryable", () => {
new ProviderError.ResponseStreamError("WebSocket closed before response.completed (code 1006: Connection ended)"),
{ providerID },
)
expect(MessageV2.APIError.isInstance(request)).toBe(true)
expect(SessionLegacy.APIError.isInstance(request)).toBe(true)
expect(SessionRetry.retryable(request, retryProvider)).toEqual({
message: "WebSocket closed before response.completed (code 1006: Connection ended)",
})
})
test("does not retry context overflow errors", () => {
const error = new MessageV2.ContextOverflowError({
const error = new SessionLegacy.ContextOverflowError({
message: "Input exceeds context window of this model",
responseBody: '{"error":{"code":"context_length_exceeded"}}',
}).toObject()
@@ -193,8 +191,8 @@ describe("session.retry.retryable", () => {
})
test("retries 500 errors even when isRetryable is false", () => {
const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
const error = Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "Internal server error",
isRetryable: false,
statusCode: 500,
@@ -206,8 +204,8 @@ describe("session.retry.retryable", () => {
})
test("retries 502 bad gateway errors", () => {
const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
const error = Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "Bad gateway",
isRetryable: false,
statusCode: 502,
@@ -218,8 +216,8 @@ describe("session.retry.retryable", () => {
})
test("retries 503 service unavailable errors", () => {
const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
const error = Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "Service unavailable",
isRetryable: false,
statusCode: 503,
@@ -230,8 +228,8 @@ describe("session.retry.retryable", () => {
})
test("does not retry 4xx errors when isRetryable is false", () => {
const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
const error = Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "Bad request",
isRetryable: false,
statusCode: 400,
@@ -242,8 +240,8 @@ describe("session.retry.retryable", () => {
})
test("retries ZlibError decompression failures", () => {
const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
const error = Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "Response decompression failed",
isRetryable: true,
metadata: { code: "ZlibError" },
@@ -256,8 +254,8 @@ describe("session.retry.retryable", () => {
})
test("maps free limits to Go upsell action", () => {
const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
const error = Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "Free usage exceeded",
isRetryable: true,
statusCode: 429,
@@ -282,8 +280,8 @@ describe("session.retry.retryable", () => {
})
test("maps Go subscription limits to workspace PAYG upsell", () => {
const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
const error = Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "Subscription quota exceeded. You can continue using free models.",
isRetryable: true,
statusCode: 429,
@@ -320,8 +318,8 @@ describe("session.retry.retryable", () => {
})
test("maps Go subscription limits without limit metadata", () => {
const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
const error = Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "Subscription quota exceeded. You can continue using free models.",
isRetryable: true,
statusCode: 429,
@@ -375,8 +373,8 @@ describe("session.message-v2.fromError", () => {
const result = MessageV2.fromError(error, { providerID })
expect(MessageV2.APIError.isInstance(result)).toBe(true)
if (!MessageV2.APIError.isInstance(result)) throw new Error("expected APIError")
expect(SessionLegacy.APIError.isInstance(result)).toBe(true)
if (!SessionLegacy.APIError.isInstance(result)) throw new Error("expected APIError")
expect(result.data.isRetryable).toBe(true)
expect(result.data.message).toBe("Connection reset by server")
expect(result.data.metadata?.code).toBe("ECONNRESET")
@@ -386,8 +384,8 @@ describe("session.message-v2.fromError", () => {
)
test("ECONNRESET socket error is retryable", () => {
const error = Schema.decodeUnknownSync(MessageV2.APIError.Schema)(
new MessageV2.APIError({
const error = Schema.decodeUnknownSync(SessionLegacy.APIError.Schema)(
new SessionLegacy.APIError({
message: "Connection reset by server",
isRetryable: true,
metadata: { code: "ECONNRESET", message: "The socket connection was closed unexpectedly" },
@@ -409,8 +407,8 @@ describe("session.message-v2.fromError", () => {
responseBody: '{"error":"boom"}',
isRetryable: false,
})
const result = MessageV2.fromError(error, { providerID: ProviderID.make("openai") })
if (!MessageV2.APIError.isInstance(result)) throw new Error("expected APIError")
const result = MessageV2.fromError(error, { providerID: ProviderV2.ID.make("openai") })
if (!SessionLegacy.APIError.isInstance(result)) throw new Error("expected APIError")
expect(result.data.isRetryable).toBe(true)
})
@@ -428,11 +426,11 @@ describe("session.message-v2.fromError", () => {
},
}),
},
{ providerID: ProviderID.make("openai") },
{ providerID: ProviderV2.ID.make("openai") },
)
expect(MessageV2.APIError.isInstance(result)).toBe(true)
if (!MessageV2.APIError.isInstance(result)) throw new Error("expected APIError")
expect(SessionLegacy.APIError.isInstance(result)).toBe(true)
if (!SessionLegacy.APIError.isInstance(result)) throw new Error("expected APIError")
expect(result.data.isRetryable).toBe(true)
expect(SessionRetry.retryable(result, retryProvider)).toEqual({
message: "An error occurred while processing your request.",