refactor(schema): extract shared public schemas (#33571)

This commit is contained in:
Kit Langton 2026-06-24 04:31:06 +02:00 committed by GitHub
commit 516cfe4e09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
130 changed files with 2014 additions and 1593 deletions

View file

@ -61,7 +61,7 @@ describe("CatalogV2", () => {
yield* credentials.create({
integrationID,
label: "First",
value: new Credential.Key({ type: "key", key: "first", metadata: { tenant: "one" } }),
value: Credential.Key.make({ type: "key", key: "first", metadata: { tenant: "one" } }),
})
expect((yield* catalog.provider.available()).map((provider) => provider.id)).toEqual([ProviderV2.ID.make("test")])
@ -69,7 +69,7 @@ describe("CatalogV2", () => {
yield* credentials.create({
integrationID,
label: "Second",
value: new Credential.Key({ type: "key", key: "second", metadata: { tenant: "two" } }),
value: Credential.Key.make({ type: "key", key: "second", metadata: { tenant: "two" } }),
})
expect((yield* catalog.provider.available()).map((provider) => provider.id)).toEqual([ProviderV2.ID.make("test")])
expect(required(yield* catalog.provider.get(ProviderV2.ID.make("test"))).request.body).toEqual({})
@ -98,7 +98,7 @@ describe("CatalogV2", () => {
yield* (yield* Credential.Service).create({
integrationID,
value: new Credential.Key({ type: "key", key: "secret" }),
value: Credential.Key.make({ type: "key", key: "secret" }),
})
expect((yield* catalog.provider.available()).map((provider) => provider.id)).toEqual([providerID])

View file

@ -27,7 +27,7 @@ describe("CommandV2", () => {
})
expect(yield* command.get("review")).toEqual(
new CommandV2.Info({
CommandV2.Info.make({
name: "review",
template: "Second",
description: "Review code",
@ -39,7 +39,7 @@ describe("CommandV2", () => {
}),
)
expect(yield* command.list()).toEqual([
new CommandV2.Info({
CommandV2.Info.make({
name: "review",
template: "Second",
description: "Review code",

View file

@ -59,7 +59,7 @@ Review files`,
)
expect(yield* command.list()).toEqual([
new CommandV2.Info({
CommandV2.Info.make({
name: "review",
template: "Review files",
description: "File review",
@ -71,8 +71,8 @@ Review files`,
},
subtask: true,
}),
new CommandV2.Info({ name: "empty", template: "" }),
new CommandV2.Info({ name: "nested/docs", template: "Write docs" }),
CommandV2.Info.make({ name: "empty", template: "" }),
CommandV2.Info.make({ name: "nested/docs", template: "Write docs" }),
])
}),
),

View file

@ -59,21 +59,21 @@ describe("ConfigSkillPlugin.Plugin", () => {
)
expect(sources).toEqual([
new SkillV2.DirectorySource({
SkillV2.DirectorySource.make({
type: "directory",
path: AbsolutePath.make(path.join("/repo/.opencode", "skill")),
}),
new SkillV2.DirectorySource({
SkillV2.DirectorySource.make({
type: "directory",
path: AbsolutePath.make(path.join("/repo/.opencode", "skills")),
}),
new SkillV2.DirectorySource({ type: "directory", path: AbsolutePath.make(path.join(directory, "skills")) }),
new SkillV2.DirectorySource({
SkillV2.DirectorySource.make({ type: "directory", path: AbsolutePath.make(path.join(directory, "skills")) }),
SkillV2.DirectorySource.make({
type: "directory",
path: AbsolutePath.make(path.join("/home/test", "shared-skills")),
}),
new SkillV2.DirectorySource({ type: "directory", path: AbsolutePath.make("/opt/skills") }),
new SkillV2.UrlSource({ type: "url", url: "https://example.test/skills/" }),
SkillV2.DirectorySource.make({ type: "directory", path: AbsolutePath.make("/opt/skills") }),
SkillV2.UrlSource.make({ type: "url", url: "https://example.test/skills/" }),
])
}),
)

View file

@ -14,7 +14,7 @@ describe("Credential", () => {
const created = yield* credentials.create({
integrationID,
label: "Work",
value: new Credential.Key({ type: "key", key: "secret" }),
value: Credential.Key.make({ type: "key", key: "secret" }),
})
expect(yield* credentials.list(integrationID)).toEqual([created])
@ -24,7 +24,7 @@ describe("Credential", () => {
const replacement = yield* credentials.create({
integrationID,
label: "Replacement",
value: new Credential.Key({ type: "key", key: "replacement" }),
value: Credential.Key.make({ type: "key", key: "replacement" }),
})
expect(yield* credentials.list(integrationID)).toEqual([replacement])

View file

@ -4,9 +4,8 @@ import { EventV2 } from "@opencode-ai/core/event"
import { Database } from "@opencode-ai/core/database/database"
import { EventSequenceTable, EventTable } from "@opencode-ai/core/event/sql"
import { Location } from "@opencode-ai/core/location"
import { AbsolutePath } from "@opencode-ai/core/schema"
import { AbsolutePath, DateTimeUtcFromMillis } from "@opencode-ai/core/schema"
import { WorkspaceV2 } from "@opencode-ai/core/workspace"
import { V2Schema } from "@opencode-ai/core/v2-schema"
import { eq } from "drizzle-orm"
import { location } from "./fixture/location"
import { testEffect } from "./lib/effect"
@ -79,24 +78,11 @@ const SyncTimestamp = EventV2.define({
},
schema: {
id: Schema.String,
timestamp: V2Schema.DateTimeUtcFromMillis,
timestamp: DateTimeUtcFromMillis,
},
})
describe("EventV2", () => {
it.effect("derives stable namespaced external IDs", () =>
Effect.sync(() => {
const input = { namespace: "opencord.agent-input", key: "input-1" }
expect(EventV2.ID.fromExternal(input)).toBe(EventV2.ID.fromExternal(input))
expect(EventV2.ID.fromExternal(input)).toMatch(/^evt_[a-f0-9]{64}$/)
expect(EventV2.ID.fromExternal({ ...input, namespace: "another-app" })).not.toBe(EventV2.ID.fromExternal(input))
expect(EventV2.ID.fromExternal({ namespace: "a:b", key: "c" })).not.toBe(
EventV2.ID.fromExternal({ namespace: "a", key: "b:c" }),
)
}),
)
it.effect("publishes events with the current location", () =>
Effect.gen(function* () {
const events = yield* EventV2.Service

View file

@ -125,7 +125,7 @@ describe("Integration", () => {
expect.objectContaining({
integrationID,
label: "Work",
value: new Credential.Key({ type: "key", key: "secret" }),
value: Credential.Key.make({ type: "key", key: "secret" }),
}),
])
expect((yield* Fiber.join(updated)).length).toBe(1)
@ -149,7 +149,7 @@ describe("Integration", () => {
instructions: "Paste the code",
callback: (code: string) =>
Effect.succeed(
new Credential.OAuth({
Credential.OAuth.make({
type: "oauth",
methodID,
access: "access",
@ -175,7 +175,7 @@ describe("Integration", () => {
expect.objectContaining({
integrationID,
label: "Personal",
value: new Credential.OAuth({
value: Credential.OAuth.make({
type: "oauth",
methodID,
access: "access",
@ -238,7 +238,7 @@ describe("Integration", () => {
url: "https://example.com/authorize",
instructions: "Sign in",
callback: Effect.succeed(
new Credential.OAuth({ type: "oauth", methodID, access: "access", refresh: "refresh", expires: 1 }),
Credential.OAuth.make({ type: "oauth", methodID, access: "access", refresh: "refresh", expires: 1 }),
),
}),
}),
@ -315,12 +315,12 @@ describe("Integration", () => {
const work = yield* credentials.create({
integrationID,
label: "Work",
value: new Credential.Key({ type: "key", key: "a" }),
value: Credential.Key.make({ type: "key", key: "a" }),
})
const personal = yield* credentials.create({
integrationID,
label: "Personal",
value: new Credential.Key({ type: "key", key: "b" }),
value: Credential.Key.make({ type: "key", key: "b" }),
})
// Stored credentials and detected env vars appear as connections.

View file

@ -52,14 +52,28 @@ const it = testEffect(
)
describe("LocationServiceMap", () => {
it.effect("compares equivalent location refs by value", () =>
Effect.sync(() => {
const directory = AbsolutePath.make("/project")
expect(Equal.equals(Location.Ref.make({ directory }), Location.Ref.make({ directory }))).toBe(true)
expect(Hash.hash(Location.Ref.make({ directory }))).toBe(
Hash.hash(Location.Ref.make({ directory, workspaceID: undefined })),
)
}),
it.live("reuses cached services for constructed and decoded location refs", () =>
Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(dir) => Effect.promise(() => dir[Symbol.asyncDispose]()),
).pipe(
Effect.flatMap((dir) =>
Effect.scoped(
Effect.gen(function* () {
const locations = yield* LocationServiceMap
const directory = AbsolutePath.make(dir.path)
const constructed = Location.Ref.make({ directory })
const decoded = Schema.decodeUnknownSync(Location.Ref)({ directory })
expect(constructed).toEqual({ directory, workspaceID: undefined })
expect(decoded).toEqual(constructed)
expect(Equal.equals(constructed, decoded)).toBe(true)
expect(Hash.hash(constructed)).toBe(Hash.hash(decoded))
expect(yield* locations.contextEffect(constructed)).toBe(yield* locations.contextEffect(decoded))
}),
),
),
),
)
it.live("isolates location state while sharing location policy with catalog", () =>

View file

@ -176,12 +176,11 @@ export function integrationHost(integration: Integration.Interface): PluginConte
return {
...authorization,
callback: authorization.callback.pipe(
Effect.map(
(credential) =>
new Credential.OAuth({
...credential,
methodID: Integration.MethodID.make(credential.methodID),
}),
Effect.map((credential) =>
Credential.OAuth.make({
...credential,
methodID: Integration.MethodID.make(credential.methodID),
}),
),
),
}
@ -190,12 +189,11 @@ export function integrationHost(integration: Integration.Interface): PluginConte
...authorization,
callback: (code: string) =>
authorization.callback(code).pipe(
Effect.map(
(credential) =>
new Credential.OAuth({
...credential,
methodID: Integration.MethodID.make(credential.methodID),
}),
Effect.map((credential) =>
Credential.OAuth.make({
...credential,
methodID: Integration.MethodID.make(credential.methodID),
}),
),
),
}
@ -205,12 +203,11 @@ export function integrationHost(integration: Integration.Interface): PluginConte
? {
refresh: (value: Credential.OAuth) =>
refresh(value).pipe(
Effect.map(
(next) =>
new Credential.OAuth({
...next,
methodID: Integration.MethodID.make(next.methodID),
}),
Effect.map((next) =>
Credential.OAuth.make({
...next,
methodID: Integration.MethodID.make(next.methodID),
}),
),
),
}

View file

@ -26,7 +26,7 @@ describe("AlibabaPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("qwen")),
api: { id: ModelV2.ID.make("qwen"), type: "aisdk", package: "test-provider" },
}),
@ -43,7 +43,7 @@ describe("AlibabaPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("qwen")),
api: { id: ModelV2.ID.make("qwen"), type: "aisdk", package: "test-provider" },
}),
@ -60,7 +60,7 @@ describe("AlibabaPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-alibaba"), ModelV2.ID.make("qwen")),
api: { id: ModelV2.ID.make("qwen"), type: "aisdk", package: "test-provider" },
}),
@ -79,7 +79,7 @@ describe("AlibabaPlugin", () => {
const plugin = yield* PluginV2.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const item = new ModelV2.Info({
const item = ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("qwen-plus"), type: "aisdk", package: "test-provider" },
})

View file

@ -83,7 +83,7 @@ describe("AmazonBedrockPlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const bedrock = new ProviderV2.Info({
const bedrock = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.amazonBedrock),
api: { type: "aisdk", package: "@ai-sdk/amazon-bedrock" },
request: {
@ -114,7 +114,7 @@ describe("AmazonBedrockPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -139,7 +139,7 @@ describe("AmazonBedrockPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -173,7 +173,7 @@ describe("AmazonBedrockPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: {
id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
@ -197,7 +197,7 @@ describe("AmazonBedrockPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -216,7 +216,7 @@ describe("AmazonBedrockPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -235,7 +235,7 @@ describe("AmazonBedrockPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -255,7 +255,7 @@ describe("AmazonBedrockPlugin", () => {
const headers: Array<string | null> = []
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -284,7 +284,7 @@ describe("AmazonBedrockPlugin", () => {
const headers: Array<string | null> = []
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -312,7 +312,7 @@ describe("AmazonBedrockPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-5.5")),
api: {
id: ModelV2.ID.make("openai.gpt-5.5"),
@ -343,7 +343,7 @@ describe("AmazonBedrockPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-5.5")),
api: {
id: ModelV2.ID.make("openai.gpt-5.5"),
@ -355,7 +355,7 @@ describe("AmazonBedrockPlugin", () => {
options: { baseURL: "https://bedrock-mantle.us-east-2.api.aws/openai/v1", region: "us-east-2" },
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("openai.gpt-oss-safeguard-120b")),
api: {
id: ModelV2.ID.make("openai.gpt-oss-safeguard-120b"),
@ -376,7 +376,7 @@ describe("AmazonBedrockPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: {
id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
@ -407,7 +407,7 @@ describe("AmazonBedrockPlugin", () => {
const headers: Array<string | null> = []
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: {
id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"),
@ -442,7 +442,7 @@ describe("AmazonBedrockPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -450,7 +450,7 @@ describe("AmazonBedrockPlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -458,7 +458,7 @@ describe("AmazonBedrockPlugin", () => {
options: { region: "eu-west-1" },
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("global.anthropic.claude-sonnet-4-5")),
api: {
id: ModelV2.ID.make("global.anthropic.claude-sonnet-4-5"),
@ -470,7 +470,7 @@ describe("AmazonBedrockPlugin", () => {
options: { region: "eu-west-1" },
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -478,7 +478,7 @@ describe("AmazonBedrockPlugin", () => {
options: { region: "ap-northeast-1" },
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -503,7 +503,7 @@ describe("AmazonBedrockPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -589,7 +589,7 @@ describe("AmazonBedrockPlugin", () => {
yield* addPlugin()
for (const item of cases) {
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.amazonBedrock, ModelV2.ID.make(item.modelID)),
api: { id: ModelV2.ID.make(item.modelID), type: "aisdk", package: "test-provider" },
}),
@ -608,7 +608,7 @@ describe("AmazonBedrockPlugin", () => {
const calls: string[] = []
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("anthropic.claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("anthropic.claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),

View file

@ -29,7 +29,7 @@ describe("AnthropicPlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const item = new ProviderV2.Info({
const item = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.anthropic),
api: { type: "aisdk", package: "@ai-sdk/anthropic" },
request: { headers: { Existing: "1" }, body: {} },
@ -64,7 +64,7 @@ describe("AnthropicPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-anthropic"), ModelV2.ID.make("claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "@ai-sdk/anthropic" },
}),
@ -81,7 +81,7 @@ describe("AnthropicPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "@ai-sdk/anthropic" },
}),

View file

@ -87,11 +87,11 @@ describe("AzureCognitiveServicesPlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const azure = new ProviderV2.Info({
const azure = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services")),
api: { type: "aisdk", package: "@ai-sdk/openai-compatible" },
})
const openai = new ProviderV2.Info({
const openai = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.openai),
api: { type: "aisdk", package: "test-provider" },
})
@ -120,7 +120,7 @@ describe("AzureCognitiveServicesPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
}),
@ -138,7 +138,7 @@ describe("AzureCognitiveServicesPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
}),
@ -146,7 +146,7 @@ describe("AzureCognitiveServicesPlugin", () => {
options: {},
})
const ignored = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
}),
@ -166,7 +166,7 @@ describe("AzureCognitiveServicesPlugin", () => {
const sdk = fakeSelectorSdk(calls)
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("messages-deployment")),
api: { id: ModelV2.ID.make("messages-deployment"), type: "aisdk", package: "test-provider" },
}),
@ -174,7 +174,7 @@ describe("AzureCognitiveServicesPlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("chat-deployment")),
api: { id: ModelV2.ID.make("chat-deployment"), type: "aisdk", package: "test-provider" },
}),
@ -182,7 +182,7 @@ describe("AzureCognitiveServicesPlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("azure-cognitive-services"), ModelV2.ID.make("language-deployment")),
api: { id: ModelV2.ID.make("language-deployment"), type: "aisdk", package: "test-provider" },
}),

View file

@ -80,7 +80,7 @@ describe("AzurePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const azure = new ProviderV2.Info({
const azure = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.azure),
api: { type: "aisdk", package: "@ai-sdk/azure" },
request: { headers: {}, body: { resourceName: "from-config" } },
@ -103,7 +103,7 @@ describe("AzurePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const azure = new ProviderV2.Info({
const azure = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.azure),
api: { type: "aisdk", package: "@ai-sdk/azure" },
request: { headers: {}, body: { resourceName: "" } },
@ -124,7 +124,7 @@ describe("AzurePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const azure = new ProviderV2.Info({
const azure = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.azure),
api: { type: "aisdk", package: "@ai-sdk/azure" },
request: { headers: {}, body: { resourceName: " " } },
@ -147,7 +147,7 @@ describe("AzurePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
}),
@ -166,7 +166,7 @@ describe("AzurePlugin", () => {
yield* addPlugin()
const exit = yield* aisdk
.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
}),
@ -186,7 +186,7 @@ describe("AzurePlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
}),
@ -204,7 +204,7 @@ describe("AzurePlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
}),
@ -222,7 +222,7 @@ describe("AzurePlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
request: { headers: {}, body: { useCompletionUrls: true } },
@ -241,7 +241,7 @@ describe("AzurePlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
}),
@ -249,7 +249,7 @@ describe("AzurePlugin", () => {
options: {},
})
const ignored = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("deployment")),
api: { id: ModelV2.ID.make("deployment"), type: "aisdk", package: "test-provider" },
}),
@ -272,7 +272,7 @@ describe("AzurePlugin", () => {
}
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("messages-deployment")),
api: { id: ModelV2.ID.make("messages-deployment"), type: "aisdk", package: "test-provider" },
}),
@ -280,7 +280,7 @@ describe("AzurePlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.azure, ModelV2.ID.make("language-deployment")),
api: { id: ModelV2.ID.make("language-deployment"), type: "aisdk", package: "test-provider" },
}),

View file

@ -64,7 +64,7 @@ describe("CerebrasPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(
ProviderV2.ID.make("custom-cerebras"),
ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
@ -90,7 +90,7 @@ describe("CerebrasPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(
ProviderV2.ID.make("custom-cerebras"),
ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),
@ -115,7 +115,7 @@ describe("CerebrasPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(
ProviderV2.ID.make("custom-cerebras"),
ModelV2.ID.make("llama-4-scout-17b-16e-instruct"),

View file

@ -116,7 +116,7 @@ describe("CloudflareAIGatewayPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -137,7 +137,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -181,7 +181,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -210,7 +210,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -247,7 +247,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -278,7 +278,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -300,7 +300,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -323,7 +323,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -352,7 +352,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -375,7 +375,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(
ProviderV2.ID.make("cloudflare-ai-gateway"),
ModelV2.ID.make("anthropic/claude-sonnet-4-5"),
@ -410,7 +410,7 @@ describe("CloudflareAIGatewayPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-ai-gateway"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),

View file

@ -93,7 +93,7 @@ describe("CloudflareWorkersAIPlugin", () => {
yield* addPlugin()
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("cloudflare-workers-ai")))
const sdk = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
api: { id: ModelV2.ID.make("@cf/model"), ...provider.api },
}),
@ -136,7 +136,7 @@ describe("CloudflareWorkersAIPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
api: {
id: ModelV2.ID.make("@cf/model"),
@ -180,7 +180,7 @@ describe("CloudflareWorkersAIPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
api: {
id: ModelV2.ID.make("@cf/model"),
@ -212,7 +212,7 @@ describe("CloudflareWorkersAIPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
api: {
id: ModelV2.ID.make("@cf/model"),
@ -241,7 +241,7 @@ describe("CloudflareWorkersAIPlugin", () => {
const calls: string[] = []
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("@cf/api-model"), type: "aisdk", package: "test-provider" },
}),
@ -260,7 +260,7 @@ describe("CloudflareWorkersAIPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("@cf/model")),
api: {
id: ModelV2.ID.make("@cf/model"),

View file

@ -54,7 +54,7 @@ describe("CoherePlugin", () => {
yield* addPlugin()
const ignored = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cohere"), ModelV2.ID.make("command")),
api: { id: ModelV2.ID.make("command"), type: "aisdk", package: "test-provider" },
}),
@ -64,7 +64,7 @@ describe("CoherePlugin", () => {
expect(ignored.sdk).toBeUndefined()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cohere"), ModelV2.ID.make("command")),
api: { id: ModelV2.ID.make("command"), type: "aisdk", package: "test-provider" },
}),
@ -81,7 +81,7 @@ describe("CoherePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-cohere"), ModelV2.ID.make("command-r-plus")),
api: { id: ModelV2.ID.make("command-r-plus"), type: "aisdk", package: "test-provider" },
}),
@ -106,7 +106,7 @@ describe("CoherePlugin", () => {
const sdk = fakeSelectorSdk(calls)
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cohere"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("command-r-plus"), type: "aisdk", package: "test-provider" },
}),

View file

@ -46,7 +46,7 @@ describe("DeepInfraPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
}),
@ -64,7 +64,7 @@ describe("DeepInfraPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-deepinfra"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
}),
@ -83,7 +83,7 @@ describe("DeepInfraPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
}),
@ -109,7 +109,7 @@ describe("DeepInfraPlugin", () => {
yield* Effect.forEach(packages, (item) =>
Effect.gen(function* () {
const ignored = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
}),
@ -120,7 +120,7 @@ describe("DeepInfraPlugin", () => {
}),
)
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "@ai-sdk/deepinfra" },
}),
@ -139,7 +139,7 @@ describe("DeepInfraPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const sdkEvent = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("deepinfra"), ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct")),
api: {
id: ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct"),

View file

@ -51,7 +51,7 @@ describe("DynamicProviderPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("test-model")),
api: { id: ModelV2.ID.make("test-model"), type: "aisdk", package: fixtureProvider },
}),
@ -69,7 +69,7 @@ describe("DynamicProviderPlugin", () => {
const sdk = { marker: "existing" }
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("test-model")),
api: { id: ModelV2.ID.make("test-model"), type: "aisdk", package: fixtureProvider },
}),
@ -86,7 +86,7 @@ describe("DynamicProviderPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-provider"), ModelV2.ID.make("test-model")),
api: { id: ModelV2.ID.make("test-model"), type: "aisdk", package: fixtureProvider },
}),
@ -102,7 +102,7 @@ describe("DynamicProviderPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin(npmEntrypoint(fixtureProviderPath))
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("npm-provider"), ModelV2.ID.make("test-model")),
api: { id: ModelV2.ID.make("test-model"), type: "aisdk", package: "fixture-provider" },
}),
@ -119,7 +119,7 @@ describe("DynamicProviderPlugin", () => {
yield* addPlugin(npmEntrypoint())
const exit = yield* aisdk
.language(
new ModelV2.Info({
ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("missing-entrypoint"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("alias"), type: "aisdk", package: "fixture-provider" },
}),
@ -136,7 +136,7 @@ describe("DynamicProviderPlugin", () => {
yield* addPlugin()
const exit = yield* aisdk
.language(
new ModelV2.Info({
ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("bad-import"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("alias"), type: "aisdk", package: "file:///missing/provider-factory.js" },
}),
@ -155,7 +155,7 @@ describe("DynamicProviderPlugin", () => {
yield* addPlugin(npmEntrypoint(tmp.entrypoint))
const exit = yield* aisdk
.language(
new ModelV2.Info({
ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("missing-factory"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("alias"), type: "aisdk", package: "fixture-provider" },
}),
@ -172,7 +172,7 @@ describe("DynamicProviderPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const language = yield* aisdk.language(
new ModelV2.Info({
ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("test-model-api"), type: "aisdk", package: fixtureProvider },
}),

View file

@ -43,7 +43,7 @@ describe("GatewayPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gateway"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -63,7 +63,7 @@ describe("GatewayPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("vercel"), ModelV2.ID.make("anthropic/claude-sonnet-4")),
api: {
id: ModelV2.ID.make("anthropic/claude-sonnet-4"),
@ -89,7 +89,7 @@ describe("GatewayPlugin", () => {
for (const modelID of vercelGatewayModels) {
const ignored = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("vercel"), ModelV2.ID.make(modelID)),
api: { id: ModelV2.ID.make(modelID), type: "aisdk", package: "test-provider" },
}),
@ -99,7 +99,7 @@ describe("GatewayPlugin", () => {
expect(ignored.sdk).toBeUndefined()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("vercel"), ModelV2.ID.make(modelID)),
api: { id: ModelV2.ID.make(modelID), type: "aisdk", package: "test-provider" },
}),

View file

@ -45,7 +45,7 @@ describe("GithubCopilotPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const ignored = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5")),
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -53,7 +53,7 @@ describe("GithubCopilotPlugin", () => {
options: { name: "github-copilot" },
})
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5")),
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -72,7 +72,7 @@ describe("GithubCopilotPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("claude-sonnet-4")),
api: { id: ModelV2.ID.make("claude-sonnet-4"), type: "aisdk", package: "test-provider" },
}),
@ -90,7 +90,7 @@ describe("GithubCopilotPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("claude-sonnet-4"), type: "aisdk", package: "test-provider" },
}),
@ -108,7 +108,7 @@ describe("GithubCopilotPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5")),
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -116,7 +116,7 @@ describe("GithubCopilotPlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5.1-codex")),
api: { id: ModelV2.ID.make("gpt-5.1-codex"), type: "aisdk", package: "test-provider" },
}),
@ -124,7 +124,7 @@ describe("GithubCopilotPlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-4o")),
api: { id: ModelV2.ID.make("gpt-4o"), type: "aisdk", package: "test-provider" },
}),
@ -132,7 +132,7 @@ describe("GithubCopilotPlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5-mini")),
api: { id: ModelV2.ID.make("gpt-5-mini"), type: "aisdk", package: "test-provider" },
}),
@ -140,7 +140,7 @@ describe("GithubCopilotPlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("gpt-5-mini-2025-08-07")),
api: { id: ModelV2.ID.make("gpt-5-mini-2025-08-07"), type: "aisdk", package: "test-provider" },
}),
@ -164,7 +164,7 @@ describe("GithubCopilotPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("default")),
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -172,7 +172,7 @@ describe("GithubCopilotPlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("small")),
api: { id: ModelV2.ID.make("gpt-5-mini"), type: "aisdk", package: "test-provider" },
}),
@ -180,7 +180,7 @@ describe("GithubCopilotPlugin", () => {
options: {},
})
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("github-copilot"), ModelV2.ID.make("sonnet")),
api: { id: ModelV2.ID.make("claude-sonnet-4"), type: "aisdk", package: "test-provider" },
}),
@ -228,7 +228,7 @@ describe("GithubCopilotPlugin", () => {
const calls: string[] = []
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("openai"), ModelV2.ID.make("gpt-5")),
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
}),

View file

@ -68,7 +68,7 @@ describe("GitLabPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
}),
@ -105,7 +105,7 @@ describe("GitLabPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
}),
@ -130,7 +130,7 @@ describe("GitLabPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
}),
@ -171,7 +171,7 @@ describe("GitLabPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
}),
@ -190,7 +190,7 @@ describe("GitLabPlugin", () => {
const calls: [string, unknown][] = []
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("duo-workflow-custom")),
api: { id: ModelV2.ID.make("duo-workflow-custom"), type: "aisdk", package: "test-provider" },
request: {
@ -225,7 +225,7 @@ describe("GitLabPlugin", () => {
const calls: [string, unknown][] = []
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("duo-workflow-exact")),
api: { id: ModelV2.ID.make("duo-workflow-exact"), type: "aisdk", package: "test-provider" },
}),
@ -252,7 +252,7 @@ describe("GitLabPlugin", () => {
const calls: [string, unknown][] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("duo-workflow-custom")),
api: { id: ModelV2.ID.make("duo-workflow-custom"), type: "aisdk", package: "test-provider" },
request: {
@ -280,7 +280,7 @@ describe("GitLabPlugin", () => {
const calls: [string, unknown][] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("gitlab"), ModelV2.ID.make("claude")),
api: { id: ModelV2.ID.make("claude"), type: "aisdk", package: "test-provider" },
request: { headers: { h: "v" }, body: {} },

View file

@ -116,7 +116,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin(GoogleVertexAnthropicPlugin)
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(
ProviderV2.ID.make("google-vertex-anthropic"),
ModelV2.ID.make("claude-sonnet-4-5"),
@ -142,7 +142,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin(GoogleVertexAnthropicPlugin)
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(
ProviderV2.ID.make("google-vertex-anthropic"),
ModelV2.ID.make("claude-sonnet-4-5"),
@ -165,7 +165,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin(GoogleVertexAnthropicPlugin)
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -184,7 +184,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin(GoogleVertexAnthropicPlugin)
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),
@ -202,7 +202,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
yield* addPlugin(GoogleVertexPlugin)
yield* addPlugin(GoogleVertexAnthropicPlugin)
const sdkResult = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make(" claude-sonnet-4-5 ")),
api: { id: ModelV2.ID.make(" claude-sonnet-4-5 "), type: "aisdk", package: "test-provider" },
}),
@ -210,7 +210,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
options: { name: "google-vertex", project: "project", location: "us" },
})
const languageResult = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make(" claude-sonnet-4-5 ")),
api: { id: ModelV2.ID.make(" claude-sonnet-4-5 "), type: "aisdk", package: "test-provider" },
}),
@ -232,7 +232,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
const calls: string[] = []
yield* addPlugin(GoogleVertexAnthropicPlugin)
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex-anthropic"), ModelV2.ID.make(" claude-sonnet-4-5 ")),
api: { id: ModelV2.ID.make(" claude-sonnet-4-5 "), type: "aisdk", package: "test-provider" },
}),
@ -250,7 +250,7 @@ describe("GoogleVertexAnthropicPlugin", () => {
const calls: string[] = []
yield* addPlugin(GoogleVertexAnthropicPlugin)
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("claude-sonnet-4-5")),
api: { id: ModelV2.ID.make("claude-sonnet-4-5"), type: "aisdk", package: "test-provider" },
}),

View file

@ -170,7 +170,7 @@ describe("GoogleVertexPlugin", () => {
yield* addPlugin()
const provider = required(yield* catalog.provider.get(ProviderV2.ID.make("google-vertex")))
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini")),
api: {
id: ModelV2.ID.make("gemini"),
@ -295,7 +295,7 @@ describe("GoogleVertexPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini")),
api: {
id: ModelV2.ID.make("gemini"),
@ -343,7 +343,7 @@ describe("GoogleVertexPlugin", () => {
Effect.void,
() =>
aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make("gemini")),
api: {
id: ModelV2.ID.make("gemini"),
@ -374,7 +374,7 @@ describe("GoogleVertexPlugin", () => {
const calls: string[] = []
yield* addPlugin()
yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google-vertex"), ModelV2.ID.make(" gemini-2.5-pro ")),
api: { id: ModelV2.ID.make(" gemini-2.5-pro "), type: "aisdk", package: "test-provider" },
}),

View file

@ -25,7 +25,7 @@ describe("GooglePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-google"), ModelV2.ID.make("gemini")),
api: { id: ModelV2.ID.make("gemini"), type: "aisdk", package: "@ai-sdk/google" },
}),
@ -43,7 +43,7 @@ describe("GooglePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("google"), ModelV2.ID.make("gemini")),
api: { id: ModelV2.ID.make("gemini"), type: "aisdk", package: "@ai-sdk/google" },
}),
@ -60,7 +60,7 @@ describe("GooglePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const sdkEvent = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-google"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("gemini-api"), type: "aisdk", package: "@ai-sdk/google" },
}),

View file

@ -26,7 +26,7 @@ describe("GroqPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("llama")),
api: { id: ModelV2.ID.make("llama"), type: "aisdk", package: "@ai-sdk/groq" },
}),
@ -43,7 +43,7 @@ describe("GroqPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("llama")),
api: { id: ModelV2.ID.make("llama"), type: "aisdk", package: "@ai-sdk/groq" },
}),
@ -60,7 +60,7 @@ describe("GroqPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("llama")),
api: { id: ModelV2.ID.make("llama"), type: "aisdk", package: "@ai-sdk/groq" },
}),
@ -77,7 +77,7 @@ describe("GroqPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-groq"), ModelV2.ID.make("llama")),
api: { id: ModelV2.ID.make("llama"), type: "aisdk", package: "@ai-sdk/groq" },
}),
@ -102,7 +102,7 @@ describe("GroqPlugin", () => {
name: string
})
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("groq"), ModelV2.ID.make("alias")),
api: {
id: ModelV2.ID.make("llama-api"),

View file

@ -26,7 +26,7 @@ describe("MistralPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("mistral-large")),
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
}),
@ -43,7 +43,7 @@ describe("MistralPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("mistral-large")),
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
}),
@ -66,7 +66,7 @@ describe("MistralPlugin", () => {
}),
)
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("mistral-large")),
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
}),
@ -90,7 +90,7 @@ describe("MistralPlugin", () => {
}),
)
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-mistral"), ModelV2.ID.make("mistral-large")),
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
}),
@ -114,7 +114,7 @@ describe("MistralPlugin", () => {
}
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("mistral"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("mistral-large"), type: "aisdk", package: "test-provider" },
}),

View file

@ -25,7 +25,7 @@ describe("OpenAICompatiblePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const defaulted = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -33,7 +33,7 @@ describe("OpenAICompatiblePlugin", () => {
options: { name: "custom" },
})
const disabled = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -51,7 +51,7 @@ describe("OpenAICompatiblePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -74,7 +74,7 @@ describe("OpenAICompatiblePlugin", () => {
}),
)
yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-provider"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -94,7 +94,7 @@ describe("OpenAICompatiblePlugin", () => {
})
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),

View file

@ -65,7 +65,7 @@ describe("OpenAIPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-openai"), ModelV2.ID.make("gpt-5")),
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -82,7 +82,7 @@ describe("OpenAIPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("gpt-5")),
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -100,7 +100,7 @@ describe("OpenAIPlugin", () => {
const calls: string[] = []
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -119,7 +119,7 @@ describe("OpenAIPlugin", () => {
const calls: string[] = []
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.anthropic, ModelV2.ID.make("gpt-5")),
api: { id: ModelV2.ID.make("gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -135,7 +135,7 @@ describe("OpenAIPlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const item = new ProviderV2.Info({
const item = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.openai),
api: { type: "aisdk", package: "@ai-sdk/openai" },
})
@ -157,7 +157,7 @@ describe("OpenAIPlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const item = new ProviderV2.Info({
const item = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.make("custom-openai")),
api: { type: "aisdk", package: "test-provider" },
})

View file

@ -120,7 +120,7 @@ describe("OpencodePlugin", () => {
})
yield* credentials.create({
integrationID: Integration.ID.make("opencode"),
value: new Credential.Key({
value: Credential.Key.make({
type: "key",
key: "secret",
metadata: { server: server.url.origin },
@ -176,11 +176,11 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = new ProviderV2.Info({
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
api: { type: "aisdk", package: "test-provider" },
})
const model = new ModelV2.Info({
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
cost: cost(1),
@ -202,11 +202,11 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = new ProviderV2.Info({
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
api: { type: "aisdk", package: "test-provider" },
})
const model = new ModelV2.Info({
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("free")),
api: { id: ModelV2.ID.make("free"), type: "aisdk", package: "test-provider" },
cost: cost(0),
@ -228,11 +228,11 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = new ProviderV2.Info({
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
api: { type: "aisdk", package: "test-provider" },
})
const model = new ModelV2.Info({
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("output-only")),
api: { id: ModelV2.ID.make("output-only"), type: "aisdk", package: "test-provider" },
cost: cost(0, 1),
@ -256,11 +256,11 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = new ProviderV2.Info({
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
api: { type: "aisdk", package: "test-provider" },
})
const model = new ModelV2.Info({
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
cost: cost(1),
@ -289,11 +289,11 @@ describe("OpencodePlugin", () => {
})
})
yield* catalog.transform((catalog) => {
const provider = new ProviderV2.Info({
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
api: { type: "aisdk", package: "test-provider" },
})
const model = new ModelV2.Info({
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
cost: cost(1),
@ -315,7 +315,7 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = new ProviderV2.Info({
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.opencode),
api: { type: "aisdk", package: "test-provider" },
request: {
@ -323,7 +323,7 @@ describe("OpencodePlugin", () => {
body: { apiKey: "configured" },
},
})
const model = new ModelV2.Info({
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
cost: cost(1),
@ -347,11 +347,11 @@ describe("OpencodePlugin", () => {
Effect.gen(function* () {
const catalog = yield* Catalog.Service
yield* catalog.transform((catalog) => {
const provider = new ProviderV2.Info({
const provider = ProviderV2.Info.make({
...ProviderV2.Info.empty(ProviderV2.ID.openai),
api: { type: "aisdk", package: "test-provider" },
})
const model = new ModelV2.Info({
const model = ModelV2.Info.make({
...ModelV2.Info.empty(provider.id, ModelV2.ID.make("paid")),
api: { id: ModelV2.ID.make("paid"), type: "aisdk", package: "test-provider" },
cost: cost(1),

View file

@ -53,7 +53,7 @@ describe("OpenRouterPlugin", () => {
yield* addPlugin()
const ignored = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openrouter, ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),
@ -63,7 +63,7 @@ describe("OpenRouterPlugin", () => {
expect(ignored.sdk).toBeUndefined()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("openai/gpt-5")),
api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
}),

View file

@ -39,7 +39,7 @@ describe("PerplexityPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("sonar")),
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
}),
@ -56,7 +56,7 @@ describe("PerplexityPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("sonar")),
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
}),
@ -73,7 +73,7 @@ describe("PerplexityPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("sonar")),
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
}),
@ -90,7 +90,7 @@ describe("PerplexityPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-perplexity"), ModelV2.ID.make("sonar")),
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
}),
@ -108,7 +108,7 @@ describe("PerplexityPlugin", () => {
const calls: string[] = []
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("perplexity"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("sonar"), type: "aisdk", package: "test-provider" },
}),

View file

@ -47,7 +47,7 @@ function withEnv<A, E, R>(vars: Record<string, string | undefined>, effect: () =
}
function model(providerID: string) {
return new ModelV2.Info({
return ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make(providerID), ModelV2.ID.make("sap-model")),
api: { id: ModelV2.ID.make("sap-model"), type: "aisdk", package: fixtureProvider },
})

View file

@ -55,7 +55,7 @@ describe("SnowflakeCortexPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("openai"), ModelV2.ID.make("gpt-4")),
api: { id: ModelV2.ID.make("gpt-4"), type: "aisdk", package: "test-provider" },
}),
@ -73,7 +73,7 @@ describe("SnowflakeCortexPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
}),
@ -92,7 +92,7 @@ describe("SnowflakeCortexPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
}),
@ -115,7 +115,7 @@ describe("SnowflakeCortexPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
}),
@ -134,7 +134,7 @@ describe("SnowflakeCortexPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
}),
@ -157,7 +157,7 @@ describe("SnowflakeCortexPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("snowflake-cortex"), ModelV2.ID.make("claude-sonnet-4-6")),
api: { id: ModelV2.ID.make("claude-sonnet-4-6"), type: "aisdk", package: "test-provider" },
}),

View file

@ -39,7 +39,7 @@ describe("TogetherAIPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("togetherai"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -57,7 +57,7 @@ describe("TogetherAIPlugin", () => {
yield* addPlugin()
const ignored = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("togetherai"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -67,7 +67,7 @@ describe("TogetherAIPlugin", () => {
expect(ignored.sdk).toBeUndefined()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("togetherai"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -85,7 +85,7 @@ describe("TogetherAIPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-togetherai"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -105,7 +105,7 @@ describe("TogetherAIPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(
ProviderV2.ID.make("togetherai"),
ModelV2.ID.make("meta-llama/Llama-3.3-70B-Instruct-Turbo"),

View file

@ -39,7 +39,7 @@ describe("VenicePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -56,7 +56,7 @@ describe("VenicePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-venice"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -74,7 +74,7 @@ describe("VenicePlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const similar = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -82,7 +82,7 @@ describe("VenicePlugin", () => {
options: { name: "venice" },
})
const other = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("model")),
api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
}),
@ -101,7 +101,7 @@ describe("VenicePlugin", () => {
const calls: string[] = []
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("venice"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("alias"), type: "aisdk", package: "test-provider" },
}),

View file

@ -60,7 +60,7 @@ describe("VercelPlugin", () => {
const aisdk = yield* AISDK.Service
yield* addPlugin()
const event = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-vercel"), ModelV2.ID.make("v0-1.0-md")),
api: { id: ModelV2.ID.make("v0-1.0-md"), type: "aisdk", package: "@ai-sdk/vercel" },
}),

View file

@ -40,7 +40,7 @@ describe("XAIPlugin", () => {
yield* addPlugin()
const ignored = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("grok-4")),
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
}),
@ -49,7 +49,7 @@ describe("XAIPlugin", () => {
})
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("grok-4")),
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
}),
@ -69,7 +69,7 @@ describe("XAIPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runSDK({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("custom-xai"), ModelV2.ID.make("grok-4")),
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
}),
@ -89,7 +89,7 @@ describe("XAIPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.make("xai"), ModelV2.ID.make("alias")),
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
}),
@ -110,7 +110,7 @@ describe("XAIPlugin", () => {
yield* addPlugin()
const result = yield* aisdk.runLanguage({
model: new ModelV2.Info({
model: ModelV2.Info.make({
...ModelV2.Info.empty(ProviderV2.ID.openai, ModelV2.ID.make("grok-4")),
api: { id: ModelV2.ID.make("grok-4"), type: "aisdk", package: "@ai-sdk/xai" },
}),

View file

@ -26,7 +26,7 @@ describe("ReferenceGuidance", () => {
name: "docs",
path: AbsolutePath.make("/docs"),
description: "Use for product documentation",
source: new Reference.LocalSource({
source: Reference.LocalSource.make({
type: "local",
path: AbsolutePath.make("/docs"),
description: "Use for product documentation",
@ -63,7 +63,7 @@ describe("ReferenceGuidance", () => {
new Reference.Info({
name: "docs",
path: AbsolutePath.make("/docs"),
source: new Reference.LocalSource({ type: "local", path: AbsolutePath.make("/docs") }),
source: Reference.LocalSource.make({ type: "local", path: AbsolutePath.make("/docs") }),
}),
]),
}),

View file

@ -18,7 +18,7 @@ describe("Reference", () => {
const references = yield* Reference.Service
const scope = yield* Scope.make()
const path = AbsolutePath.make("/docs")
const source = new Reference.LocalSource({
const source = Reference.LocalSource.make({
type: "local",
path,
description: "Use for API documentation",
@ -44,7 +44,7 @@ describe("Reference", () => {
Effect.gen(function* () {
const references = yield* Reference.Service
const repository = Repository.parseRemote("owner/repo")
const source = new Reference.GitSource({ type: "git", repository: "owner/repo", branch: "main" })
const source = Reference.GitSource.make({ type: "git", repository: "owner/repo", branch: "main" })
yield* references.transform((editor) => editor.add("sdk", source))
expect(yield* references.list()).toEqual([
@ -67,7 +67,7 @@ describe("Reference", () => {
Effect.gen(function* () {
const references = yield* Reference.Service
const repository = Repository.parseRemote("owner/repo")
const source = new Reference.GitSource({
const source = Reference.GitSource.make({
type: "git",
repository: "owner/repo",
description: "Use for SDK implementation details",

View file

@ -55,21 +55,6 @@ const location = Location.Ref.make({ directory: AbsolutePath.make("/project") })
const id = SessionV2.ID.create()
describe("SessionV2.create", () => {
it.effect("derives stable namespaced external IDs", () =>
Effect.sync(() => {
const input = { namespace: "opencord.agent-thread", key: "thread-1" }
expect(SessionV2.ID.fromExternal(input)).toBe(SessionV2.ID.fromExternal(input))
expect(SessionV2.ID.fromExternal(input)).toMatch(/^ses_[a-f0-9]{64}$/)
expect(SessionV2.ID.fromExternal({ ...input, namespace: "another-app" })).not.toBe(
SessionV2.ID.fromExternal(input),
)
expect(SessionV2.ID.fromExternal({ namespace: "a:b", key: "c" })).not.toBe(
SessionV2.ID.fromExternal({ namespace: "a", key: "b:c" }),
)
}),
)
it.effect("creates a fresh projected session when the ID is omitted", () =>
Effect.gen(function* () {
const session = yield* SessionV2.Service
@ -218,7 +203,7 @@ describe("SessionV2.create", () => {
const events = yield* EventV2.Service
const { db } = yield* Database.Service
const created = yield* session.create({ location })
yield* session.prompt({ sessionID: created.id, prompt: new Prompt({ text: "Hello" }), resume: false })
yield* session.prompt({ sessionID: created.id, prompt: Prompt.make({ text: "Hello" }), resume: false })
yield* SessionInput.promoteSteers(db, events, created.id, Number.MAX_SAFE_INTEGER)
expect(
@ -238,7 +223,7 @@ describe("SessionV2.create", () => {
const created = yield* session.create({ id: SessionV2.ID.make("ses_fresh_target_replay"), location })
const admitted = yield* session.prompt({
sessionID: created.id,
prompt: new Prompt({ text: "Replay lifecycle" }),
prompt: Prompt.make({ text: "Replay lifecycle" }),
resume: false,
})
yield* SessionInput.promoteSteers(sourceDb, sourceEvents, created.id, Number.MAX_SAFE_INTEGER)

View file

@ -36,7 +36,7 @@ const assistantRow = (
id: _,
type,
...data
} = encodeMessage(new SessionMessage.Assistant({ id, type: "assistant", agent: "build", model, content: [], time }))
} = encodeMessage(SessionMessage.Assistant.make({ id, type: "assistant", agent: "build", model, content: [], time }))
return { id, session_id: sessionID, type, seq, time_created: DateTime.toEpochMillis(time.created), data }
}
@ -69,7 +69,7 @@ describe("SessionProjector", () => {
sessionID,
messageID: SessionMessage.ID.make("msg_first"),
timestamp: created,
prompt: new Prompt({ text: "first" }),
prompt: Prompt.make({ text: "first" }),
delivery: "steer",
},
{ id: EventV2.ID.make("evt_z") },
@ -80,7 +80,7 @@ describe("SessionProjector", () => {
sessionID,
messageID: SessionMessage.ID.make("msg_second"),
timestamp: created,
prompt: new Prompt({ text: "second" }),
prompt: Prompt.make({ text: "second" }),
delivery: "steer",
},
{ id: EventV2.ID.make("evt_a") },
@ -145,7 +145,7 @@ describe("SessionProjector", () => {
const admitted = yield* SessionInput.admit(db, events, {
id,
sessionID,
prompt: new Prompt({ text: "promote me" }),
prompt: Prompt.make({ text: "promote me" }),
delivery: "steer",
})
if (!admitted) return yield* Effect.die("Prompt admission failed")
@ -154,7 +154,7 @@ describe("SessionProjector", () => {
sessionID,
timestamp: admitted.timeCreated,
messageID: id,
prompt: new Prompt({ text: "promote me" }),
prompt: Prompt.make({ text: "promote me" }),
delivery: "steer",
})
@ -334,7 +334,7 @@ describe("SessionProjector", () => {
it.effect("does not revive a stale incomplete in-memory assistant projection", () =>
Effect.gen(function* () {
const stale = new SessionMessage.Assistant({
const stale = SessionMessage.Assistant.make({
id: SessionMessage.ID.make("msg_assistant_stale"),
type: "assistant",
agent: "build",
@ -342,7 +342,7 @@ describe("SessionProjector", () => {
content: [],
time: { created },
})
const completed = new SessionMessage.Assistant({
const completed = SessionMessage.Assistant.make({
id: SessionMessage.ID.make("msg_assistant_completed"),
type: "assistant",
agent: "build",
@ -466,15 +466,15 @@ describe("SessionProjector", () => {
Schema.decodeUnknownSync(SessionMessage.Message)({ ...row.data, id: row.id, type: row.type }),
)
expect(messages).toEqual([
new SessionMessage.Assistant({
SessionMessage.Assistant.make({
id: SessionMessage.ID.make("msg_assistant_completed"),
type: "assistant",
agent: "build",
model,
content: [new SessionMessage.AssistantText({ type: "text", id: "text-stale", text: "" })],
content: [SessionMessage.AssistantText.make({ type: "text", id: "text-stale", text: "" })],
time: { created: DateTime.makeUnsafe(1), completed: DateTime.makeUnsafe(2) },
}),
new SessionMessage.Assistant({
SessionMessage.Assistant.make({
id: SessionMessage.ID.make("msg_assistant_stale"),
type: "assistant",
agent: "build",

View file

@ -147,7 +147,7 @@ describe("SessionV2.prompt", () => {
const message = yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Fix the failing tests" }),
prompt: Prompt.make({ text: "Fix the failing tests" }),
resume: false,
})
@ -171,8 +171,8 @@ describe("SessionV2.prompt", () => {
const fiber = yield* session.events({ sessionID }).pipe(Stream.take(4), Stream.runCollect, Effect.forkScoped)
yield* Effect.yieldNow
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
yield* SessionInput.promoteSteers(db, events, sessionID, Number.MAX_SAFE_INTEGER)
const streamed = Array.from(yield* Fiber.join(fiber))
@ -198,7 +198,7 @@ describe("SessionV2.prompt", () => {
const session = yield* SessionV2.Service
const message = yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Fix the failing tests" }),
prompt: Prompt.make({ text: "Fix the failing tests" }),
resume: false,
})
@ -217,7 +217,7 @@ describe("SessionV2.prompt", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
const input = { sessionID, prompt: new Prompt({ text: "Fix the failing tests" }), resume: false }
const input = { sessionID, prompt: Prompt.make({ text: "Fix the failing tests" }), resume: false }
const first = yield* session.prompt(input)
const second = yield* session.prompt(input)
@ -235,7 +235,7 @@ describe("SessionV2.prompt", () => {
const input = {
sessionID,
id: messageID,
prompt: new Prompt({ text: "Fix the failing tests" }),
prompt: Prompt.make({ text: "Fix the failing tests" }),
resume: false,
}
@ -255,7 +255,7 @@ describe("SessionV2.prompt", () => {
const input = {
sessionID,
id: messageID,
prompt: new Prompt({ text: "Recover committed prompt" }),
prompt: Prompt.make({ text: "Recover committed prompt" }),
resume: false,
}
const first = yield* session.prompt(input)
@ -276,13 +276,13 @@ describe("SessionV2.prompt", () => {
yield* session.prompt({
sessionID,
id: messageID,
prompt: new Prompt({ text: "Fix the failing tests" }),
prompt: Prompt.make({ text: "Fix the failing tests" }),
})
const failure = yield* session
.prompt({
sessionID,
id: messageID,
prompt: new Prompt({ text: "Delete the failing tests" }),
prompt: Prompt.make({ text: "Delete the failing tests" }),
resume: false,
})
.pipe(Effect.flip)
@ -301,14 +301,14 @@ describe("SessionV2.prompt", () => {
yield* session.prompt({
id: messageID,
sessionID,
prompt: new Prompt({ text: "Fix the failing tests" }),
prompt: Prompt.make({ text: "Fix the failing tests" }),
resume: false,
})
const failure = yield* session
.prompt({
id: messageID,
sessionID,
prompt: new Prompt({ text: "Fix the failing tests" }),
prompt: Prompt.make({ text: "Fix the failing tests" }),
delivery: "queue",
resume: false,
})
@ -325,7 +325,7 @@ describe("SessionV2.prompt", () => {
const input = {
sessionID,
id: messageID,
prompt: new Prompt({ text: "Fix the failing tests" }),
prompt: Prompt.make({ text: "Fix the failing tests" }),
resume: false,
}
@ -344,7 +344,7 @@ describe("SessionV2.prompt", () => {
const { db } = yield* Database.Service
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
yield* session.prompt({ id: messageID, sessionID, prompt: new Prompt({ text: "Promote once" }), resume: false })
yield* session.prompt({ id: messageID, sessionID, prompt: Prompt.make({ text: "Promote once" }), resume: false })
yield* Effect.all(
[
@ -368,9 +368,9 @@ describe("SessionV2.prompt", () => {
const { db } = yield* Database.Service
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
const first = yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Before cutoff" }), resume: false })
const first = yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Before cutoff" }), resume: false })
const cutoff = first.admittedSeq
const second = yield* session.prompt({ sessionID, prompt: new Prompt({ text: "After cutoff" }), resume: false })
const second = yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "After cutoff" }), resume: false })
yield* SessionInput.promoteSteers(db, events, sessionID, cutoff)
@ -386,7 +386,12 @@ describe("SessionV2.prompt", () => {
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
wakeCalls.length = 0
yield* session.prompt({ id: messageID, sessionID, prompt: new Prompt({ text: "Replay pending" }), resume: false })
yield* session.prompt({
id: messageID,
sessionID,
prompt: Prompt.make({ text: "Replay pending" }),
resume: false,
})
const recorded = yield* db
.select()
.from(EventTable)
@ -422,7 +427,7 @@ describe("SessionV2.prompt", () => {
yield* setup
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
const prompt = new Prompt({ text: "Historical prompt" })
const prompt = Prompt.make({ text: "Historical prompt" })
yield* events.publish(SessionEvent.Prompted, {
sessionID,
messageID,
@ -443,7 +448,7 @@ describe("SessionV2.prompt", () => {
yield* setup
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
const prompt = new Prompt({ text: "Historical queued prompt" })
const prompt = Prompt.make({ text: "Historical queued prompt" })
yield* events.publish(SessionEvent.Prompted, {
sessionID,
messageID,
@ -478,7 +483,7 @@ describe("SessionV2.prompt", () => {
.onConflictDoNothing()
.run()
.pipe(Effect.orDie)
const prompt = new Prompt({ text: "Fix the failing tests" })
const prompt = Prompt.make({ text: "Fix the failing tests" })
yield* session.prompt({ id: messageID, sessionID, prompt, resume: false })
const failure = yield* session
@ -502,7 +507,7 @@ describe("SessionV2.prompt", () => {
})
const failure = yield* session
.prompt({ id: messageID, sessionID, prompt: new Prompt({ text: "Conflicting prompt" }), resume: false })
.prompt({ id: messageID, sessionID, prompt: Prompt.make({ text: "Conflicting prompt" }), resume: false })
.pipe(Effect.flip)
expect(failure).toMatchObject({ _tag: "Session.PromptConflictError", sessionID, messageID })
@ -517,7 +522,7 @@ describe("SessionV2.prompt", () => {
executionCalls.length = 0
wakeCalls.length = 0
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Run by default" }) })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Run by default" }) })
expect(executionCalls).toEqual([])
expect(wakeCalls).toEqual([sessionID])
@ -533,7 +538,7 @@ describe("SessionV2.prompt", () => {
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Run explicitly" }),
prompt: Prompt.make({ text: "Run explicitly" }),
resume: true,
})
@ -549,7 +554,7 @@ describe("SessionV2.prompt", () => {
executionCalls.length = 0
wakeCalls.length = 0
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Do not run" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Do not run" }), resume: false })
expect(executionCalls).toEqual([])
expect(wakeCalls).toEqual([])

View file

@ -16,7 +16,7 @@ const model = Model.make({ id: "model", provider: "provider", route: OpenAIChat.
describe("toLLMMessages", () => {
test("omits empty assistant turns", () => {
const assistant = (value: string, content: SessionMessage.Assistant["content"]) =>
new SessionMessage.Assistant({
SessionMessage.Assistant.make({
id: id(value),
type: "assistant",
agent: "build",
@ -27,13 +27,13 @@ describe("toLLMMessages", () => {
const messages = toLLMMessages(
[
assistant("empty", []),
assistant("empty-text", [new SessionMessage.AssistantText({ type: "text", id: "empty", text: "" })]),
assistant("empty-text", [SessionMessage.AssistantText.make({ type: "text", id: "empty", text: "" })]),
assistant("empty-reasoning", [
new SessionMessage.AssistantReasoning({ type: "reasoning", id: "empty-reasoning", text: "" }),
SessionMessage.AssistantReasoning.make({ type: "reasoning", id: "empty-reasoning", text: "" }),
]),
assistant("text", [new SessionMessage.AssistantText({ type: "text", id: "text", text: "Partial" })]),
assistant("text", [SessionMessage.AssistantText.make({ type: "text", id: "text", text: "Partial" })]),
assistant("reasoning", [
new SessionMessage.AssistantReasoning({
SessionMessage.AssistantReasoning.make({
type: "reasoning",
id: "reasoning",
text: "",
@ -48,43 +48,43 @@ describe("toLLMMessages", () => {
})
test("maps every top-level V2 Session message type", () => {
const file = new FileAttachment({ uri: "data:image/png;base64,aGVsbG8=", mime: "image/png", name: "hello.png" })
const file = FileAttachment.make({ uri: "data:image/png;base64,aGVsbG8=", mime: "image/png", name: "hello.png" })
const messages = toLLMMessages(
[
new SessionMessage.AgentSwitched({
SessionMessage.AgentSwitched.make({
id: id("agent"),
type: "agent-switched",
agent: "build",
time: { created },
}),
new SessionMessage.ModelSwitched({
SessionMessage.ModelSwitched.make({
id: id("model"),
type: "model-switched",
model: { id: ModelV2.ID.make("model"), providerID: ProviderV2.ID.make("provider") },
time: { created },
}),
new SessionMessage.System({
SessionMessage.System.make({
id: id("system"),
type: "system",
text: "Updated context\n\nOther context",
time: { created },
}),
new SessionMessage.User({
SessionMessage.User.make({
id: id("user"),
type: "user",
text: "Inspect this image",
files: [file],
agents: [new AgentAttachment({ name: "build" })],
agents: [AgentAttachment.make({ name: "build" })],
time: { created },
}),
new SessionMessage.Synthetic({
SessionMessage.Synthetic.make({
id: id("synthetic"),
type: "synthetic",
sessionID: SessionV2.ID.make("ses_translate"),
text: "Synthetic context",
time: { created },
}),
new SessionMessage.Shell({
SessionMessage.Shell.make({
id: id("shell"),
type: "shell",
callID: "shell-1",
@ -92,7 +92,7 @@ describe("toLLMMessages", () => {
output: "/project",
time: { created, completed: created },
}),
new SessionMessage.Compaction({
SessionMessage.Compaction.make({
id: id("compaction"),
type: "compaction",
reason: "auto",
@ -142,31 +142,31 @@ Recent work
test("replays durable tool media into canonical tool messages without structured base64", () => {
const messages = toLLMMessages(
[
new SessionMessage.Assistant({
SessionMessage.Assistant.make({
id: id("assistant"),
type: "assistant",
agent: "build",
model: { id: ModelV2.ID.make("model"), providerID: ProviderV2.ID.make("provider") },
content: [
new SessionMessage.AssistantText({ type: "text", id: "text-1", text: "Checking" }),
new SessionMessage.AssistantReasoning({
SessionMessage.AssistantText.make({ type: "text", id: "text-1", text: "Checking" }),
SessionMessage.AssistantReasoning.make({
type: "reasoning",
id: "reasoning-1",
text: "Think",
providerMetadata: { anthropic: { signature: "sig_1" } },
}),
new SessionMessage.AssistantTool({
SessionMessage.AssistantTool.make({
type: "tool",
id: "pending",
name: "read",
state: new SessionMessage.ToolStatePending({ status: "pending", input: '{"path":"README.md"}' }),
state: SessionMessage.ToolStatePending.make({ status: "pending", input: '{"path":"README.md"}' }),
time: { created },
}),
new SessionMessage.AssistantTool({
SessionMessage.AssistantTool.make({
type: "tool",
id: "running",
name: "read",
state: new SessionMessage.ToolStateRunning({
state: SessionMessage.ToolStateRunning.make({
status: "running",
input: { path: "README.md" },
content: [],
@ -174,11 +174,11 @@ Recent work
}),
time: { created },
}),
new SessionMessage.AssistantTool({
SessionMessage.AssistantTool.make({
type: "tool",
id: "completed",
name: "read",
state: new SessionMessage.ToolStateCompleted({
state: SessionMessage.ToolStateCompleted.make({
status: "completed",
input: { path: "README.md" },
content: [
@ -194,7 +194,7 @@ Recent work
}),
time: { created, completed: created },
}),
new SessionMessage.AssistantTool({
SessionMessage.AssistantTool.make({
type: "tool",
id: "hosted",
name: "web_search",
@ -203,7 +203,7 @@ Recent work
metadata: { fake: { continuation: "hosted-call" } },
resultMetadata: { fake: { continuation: "hosted-result" } },
},
state: new SessionMessage.ToolStateCompleted({
state: SessionMessage.ToolStateCompleted.make({
status: "completed",
input: { query: "Effect" },
content: [{ type: "text", text: "Found it" }],
@ -211,12 +211,12 @@ Recent work
}),
time: { created, completed: created },
}),
new SessionMessage.AssistantTool({
SessionMessage.AssistantTool.make({
type: "tool",
id: "hosted-failed",
name: "write",
provider: { executed: true, metadata: { fake: { continuation: "failed" } } },
state: new SessionMessage.ToolStateError({
state: SessionMessage.ToolStateError.make({
status: "error",
input: { path: "README.md" },
content: [],
@ -299,13 +299,13 @@ Recent work
test("restores OpenAI encrypted reasoning metadata", () => {
const messages = toLLMMessages(
[
new SessionMessage.Assistant({
SessionMessage.Assistant.make({
id: id("assistant-openai-reasoning"),
type: "assistant",
agent: "build",
model: { id: ModelV2.ID.make("model"), providerID: ProviderV2.ID.make("provider") },
content: [
new SessionMessage.AssistantReasoning({
SessionMessage.AssistantReasoning.make({
type: "reasoning",
id: "reasoning-openai",
text: "Think",
@ -330,19 +330,19 @@ Recent work
test("drops provider-native continuation metadata after a model switch", () => {
const messages = toLLMMessages(
[
new SessionMessage.Assistant({
SessionMessage.Assistant.make({
id: id("assistant-old-model"),
type: "assistant",
agent: "build",
model: { id: ModelV2.ID.make("old-model"), providerID: ProviderV2.ID.make("provider") },
content: [
new SessionMessage.AssistantReasoning({
SessionMessage.AssistantReasoning.make({
type: "reasoning",
id: "reasoning-old-model",
text: "Visible thought",
providerMetadata: { anthropic: { signature: "sig_old" } },
}),
new SessionMessage.AssistantTool({
SessionMessage.AssistantTool.make({
type: "tool",
id: "hosted-old-model",
name: "web_search",
@ -351,7 +351,7 @@ Recent work
metadata: { openai: { itemId: "hosted-old-model" } },
resultMetadata: { openai: { itemId: "hosted-old-model" } },
},
state: new SessionMessage.ToolStateCompleted({
state: SessionMessage.ToolStateCompleted.make({
status: "completed",
input: { query: "Effect" },
content: [],
@ -360,7 +360,7 @@ Recent work
}),
time: { created, completed: created },
}),
new SessionMessage.AssistantTool({
SessionMessage.AssistantTool.make({
type: "tool",
id: "local-old-model",
name: "read",
@ -369,7 +369,7 @@ Recent work
metadata: { fake: { call: "old" } },
resultMetadata: { fake: { result: "old" } },
},
state: new SessionMessage.ToolStateCompleted({
state: SessionMessage.ToolStateCompleted.make({
status: "completed",
input: { path: "README.md" },
content: [],

View file

@ -22,7 +22,7 @@ type Api =
| { readonly type: "native"; readonly url?: string; readonly settings: Record<string, unknown> }
const model = (api: Api, variants: ModelV2.Info["variants"] = []) =>
new ModelV2.Info({
ModelV2.Info.make({
id: ModelV2.ID.make("test-model"),
providerID: ProviderV2.ID.make("test-provider"),
name: "Test model",
@ -79,7 +79,7 @@ describe("SessionRunnerModel", () => {
it.effect("uses merged API settings for OpenAI-compatible auth and request defaults", () =>
Effect.gen(function* () {
const resolved = yield* SessionRunnerModel.fromCatalogModel(
new ModelV2.Info({
ModelV2.Info.make({
...model({
type: "aisdk",
package: "@ai-sdk/openai-compatible",
@ -114,7 +114,7 @@ describe("SessionRunnerModel", () => {
options: { reasoningEffort: "high" },
},
])
const catalog = new ModelV2.Info({
const catalog = ModelV2.Info.make({
...base,
request: { ...base.request, options: { ...base.request.options, reasoningEffort: "medium" } },
})
@ -264,11 +264,11 @@ describe("SessionRunnerModel", () => {
it.effect("uses resolved credentials for bearer auth", () =>
Effect.gen(function* () {
const resolved = yield* SessionRunnerModel.fromCatalogModel(
new ModelV2.Info({
ModelV2.Info.make({
...model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }),
request: { headers: {}, body: {}, generation: {}, options: {} },
}),
new Credential.Key({ type: "key", key: "secret" }),
Credential.Key.make({ type: "key", key: "secret" }),
)
const request = LLM.request({ model: resolved, prompt: "Hello" })
const headers = yield* resolved.route.auth.apply({
@ -285,9 +285,9 @@ describe("SessionRunnerModel", () => {
it.effect("prefers stored credentials over configured auth", () =>
Effect.gen(function* () {
const credential = new Credential.Key({ type: "key", key: "stored-secret", metadata: { tenant: "work" } })
const credential = Credential.Key.make({ type: "key", key: "stored-secret", metadata: { tenant: "work" } })
const resolved = yield* SessionRunnerModel.fromCatalogModel(
new ModelV2.Info({
ModelV2.Info.make({
...model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }),
request: { headers: {}, body: { apiKey: "configured-secret" }, generation: {}, options: {} },
}),

View file

@ -154,7 +154,7 @@ describe("SessionRunnerLLM recorded", () => {
const session = yield* SessionV2.Service
const prompt = yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Say hello in one short sentence." }),
prompt: Prompt.make({ text: "Say hello in one short sentence." }),
resume: false,
})

View file

@ -26,7 +26,6 @@ import { SessionMessage } from "@opencode-ai/core/session/message"
import { Prompt } from "@opencode-ai/core/session/prompt"
import { SessionProjector } from "@opencode-ai/core/session/projector"
import { SessionExecution } from "@opencode-ai/core/session/execution"
import { SessionContextEpoch } from "@opencode-ai/core/session/context-epoch"
import { SessionRunCoordinator } from "@opencode-ai/core/session/run-coordinator"
import { SessionRunner } from "@opencode-ai/core/session/runner"
import * as SessionRunnerLLM from "@opencode-ai/core/session/runner/llm"
@ -351,7 +350,7 @@ const setupOverflowRecovery = Effect.gen(function* () {
response = fragmentFixture("text", "text-earlier", ["Earlier answer"]).completeEvents
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Earlier question ".repeat(700) }),
prompt: Prompt.make({ text: "Earlier question ".repeat(700) }),
resume: false,
})
yield* session.resume(sessionID)
@ -476,7 +475,7 @@ const verifyEphemeralDeltas = (kind: FragmentKind) =>
const chunks = Array.from({ length: 32 }, (_, index) => `${index},`)
const fixture = fragmentFixture(kind, fragmentID(kind, "many"), chunks)
const expectedContext = [{ type: "user", text: prompt }, fixture.expectedAssistant]
yield* session.prompt({ sessionID, prompt: new Prompt({ text: prompt }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: prompt }), resume: false })
const events = yield* EventV2.Service
const live = yield* events.subscribe(fixture.delta).pipe(Stream.take(32), Stream.runCollect, Effect.forkScoped)
yield* Effect.yieldNow
@ -507,7 +506,7 @@ const verifyPartialFlushOnFailure = (kind: FragmentKind) =>
const prompt = `Fail after ${kind}`
const fixture = fragmentFixture(kind, fragmentID(kind, "partial"), ["Partial"])
const failure = providerUnavailable()
yield* session.prompt({ sessionID, prompt: new Prompt({ text: prompt }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: prompt }), resume: false })
responseStream = Stream.concat(Stream.fromIterable(fixture.partialEvents), Stream.fail(failure))
expect(yield* session.resume(sessionID).pipe(Effect.flip)).toBe(failure)
@ -529,7 +528,7 @@ const verifyPartialFlushOnInterruption = (kind: FragmentKind) =>
const prompt = `Interrupt after ${kind}`
const fixture = fragmentFixture(kind, fragmentID(kind, "interrupted"), ["Partial"])
const streamed = yield* Deferred.make<void>()
yield* session.prompt({ sessionID, prompt: new Prompt({ text: prompt }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: prompt }), resume: false })
responseStream = Stream.concat(
Stream.fromIterable(fixture.partialEvents),
Stream.fromEffect(Deferred.succeed(streamed, undefined)).pipe(Stream.flatMap(() => Stream.never)),
@ -573,7 +572,7 @@ describe("SessionRunnerLLM", () => {
}),
}),
})
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Use application context" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Use application context" }), resume: false })
responses = [
[
LLMEvent.stepStart({ index: 0 }),
@ -621,7 +620,7 @@ describe("SessionRunnerLLM", () => {
streamStarted = undefined
response = []
const message = yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Run automatically" }) })
const message = yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Run automatically" }) })
expect(requests).toHaveLength(1)
expect(yield* session.messages({ sessionID })).toMatchObject([
@ -634,8 +633,8 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
requests.length = 0
responses = undefined
@ -662,7 +661,7 @@ describe("SessionRunnerLLM", () => {
const { db } = yield* Database.Service
const messageID = SessionMessage.ID.create()
systemUnavailable = true
yield* session.prompt({ id: messageID, sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ id: messageID, sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
const exit = yield* session.resume(sessionID).pipe(Effect.exit)
@ -680,7 +679,7 @@ describe("SessionRunnerLLM", () => {
).toBeUndefined()
systemUnavailable = false
yield* session.prompt({ id: messageID, sessionID, prompt: new Prompt({ text: "First" }) })
yield* session.prompt({ id: messageID, sessionID, prompt: Prompt.make({ text: "First" }) })
expect(requests).toHaveLength(1)
expect(requests[0]?.messages.map((message) => message.role)).toEqual(["user"])
@ -693,7 +692,7 @@ describe("SessionRunnerLLM", () => {
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
const { db } = yield* Database.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
yield* session.resume(sessionID)
@ -711,7 +710,7 @@ describe("SessionRunnerLLM", () => {
.get(),
).toBeUndefined()
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
const exit = yield* session.resume(sessionID).pipe(Effect.exit)
expect(Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause)).toBe(true)
@ -725,7 +724,7 @@ describe("SessionRunnerLLM", () => {
yield* setup
const session = yield* SessionV2.Service
const { db } = yield* Database.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
response = []
yield* session.resume(sessionID)
yield* db
@ -734,7 +733,7 @@ describe("SessionRunnerLLM", () => {
.where(eq(SessionContextEpochTable.session_id, sessionID))
.run()
.pipe(Effect.orDie)
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
requests.length = 0
const exit = yield* session.resume(sessionID).pipe(Effect.exit)
@ -749,13 +748,13 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
yield* session.resume(sessionID)
systemBaseline = "Changed context"
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
@ -790,7 +789,7 @@ describe("SessionRunnerLLM", () => {
}),
)
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = fragmentFixture("text", "text-build", ["Done"]).completeEvents
@ -816,7 +815,7 @@ describe("SessionRunnerLLM", () => {
editor.default(AgentV2.ID.make("reviewer"))
})
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = fragmentFixture("text", "text-reviewer", ["Done"]).completeEvents
@ -845,7 +844,7 @@ describe("SessionRunnerLLM", () => {
.run()
.pipe(Effect.orDie)
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = fragmentFixture("text", "text-selected", ["Done"]).completeEvents
@ -862,7 +861,7 @@ describe("SessionRunnerLLM", () => {
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
skillBaselines.set(AgentV2.ID.make("build"), "Build skills")
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
@ -874,7 +873,7 @@ describe("SessionRunnerLLM", () => {
timestamp: DateTime.makeUnsafe(1),
agent: "reviewer",
})
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
@ -905,7 +904,7 @@ describe("SessionRunnerLLM", () => {
})
.pipe(Effect.asVoid)
})
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
@ -935,7 +934,7 @@ describe("SessionRunnerLLM", () => {
})
.pipe(Effect.asVoid)
})
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
@ -949,13 +948,13 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
yield* session.resume(sessionID)
systemRemoved = true
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
yield* session.resume(sessionID)
expect(requests[1]?.messages.map((message) => message.role)).toEqual(["user", "user", "system"])
@ -971,13 +970,13 @@ describe("SessionRunnerLLM", () => {
yield* setup
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
yield* session.resume(sessionID)
systemBaseline = "Changed context"
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
yield* session.resume(sessionID)
yield* events.publish(SessionEvent.ModelSwitched, {
sessionID,
@ -986,7 +985,7 @@ describe("SessionRunnerLLM", () => {
model: { id: ModelV2.ID.make("replacement"), providerID: ProviderV2.ID.make("fake") },
})
systemBaseline = "Replacement context"
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Third" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Third" }), resume: false })
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
@ -1006,7 +1005,7 @@ describe("SessionRunnerLLM", () => {
])
yield* replaySessionProjection(sessionID)
expect(yield* session.messages({ sessionID })).toHaveLength(6)
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Fourth" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Fourth" }), resume: false })
yield* session.resume(sessionID)
}),
)
@ -1016,7 +1015,7 @@ describe("SessionRunnerLLM", () => {
yield* setup
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
@ -1028,11 +1027,11 @@ describe("SessionRunnerLLM", () => {
model: { id: ModelV2.ID.make("replacement"), providerID: ProviderV2.ID.make("fake") },
})
systemUnavailable = true
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
yield* session.resume(sessionID)
systemUnavailable = false
systemBaseline = "Replacement context"
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Third" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Third" }), resume: false })
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
@ -1048,7 +1047,7 @@ describe("SessionRunnerLLM", () => {
yield* setup
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
@ -1069,7 +1068,7 @@ describe("SessionRunnerLLM", () => {
recent: "",
})
systemBaseline = "Replacement context"
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
yield* session.resume(sessionID)
expect(requests.map((request) => request.system.map((part) => part.text))).toEqual([
@ -1077,7 +1076,7 @@ describe("SessionRunnerLLM", () => {
["Replacement context"],
])
yield* replaySessionProjection(sessionID)
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Third" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Third" }), resume: false })
yield* session.resume(sessionID)
}),
)
@ -1089,7 +1088,7 @@ describe("SessionRunnerLLM", () => {
response = fragmentFixture("text", "text-first", ["Earlier answer"]).completeEvents
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Earlier question ".repeat(180) }),
prompt: Prompt.make({ text: "Earlier question ".repeat(180) }),
resume: false,
})
yield* session.resume(sessionID)
@ -1102,7 +1101,7 @@ describe("SessionRunnerLLM", () => {
]
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Recent exact request ".repeat(180) }),
prompt: Prompt.make({ text: "Recent exact request ".repeat(180) }),
resume: false,
})
yield* session.resume(sessionID)
@ -1128,7 +1127,7 @@ describe("SessionRunnerLLM", () => {
]
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Newest exact request ".repeat(180) }),
prompt: Prompt.make({ text: "Newest exact request ".repeat(180) }),
resume: false,
})
yield* session.resume(sessionID)
@ -1156,7 +1155,7 @@ describe("SessionRunnerLLM", () => {
fragmentFixture("text", "text-summary", ["## Goal\n- Recover overflow"]).completeEvents,
fragmentFixture("text", "text-final", ["Recovered"]).completeEvents,
]
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Continue" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
yield* session.resume(sessionID)
expect(requests).toHaveLength(3)
@ -1186,7 +1185,7 @@ describe("SessionRunnerLLM", () => {
fragmentFixture("text", "text-summary", ["## Goal\n- Recover once"]).completeEvents,
overflow(),
]
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Continue" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
yield* session.resume(sessionID)
expect(requests).toHaveLength(3)
@ -1214,7 +1213,7 @@ describe("SessionRunnerLLM", () => {
fragmentFixture("text", "text-summary", ["## Goal\n- Recover raw overflow"]).completeEvents,
fragmentFixture("text", "text-final", ["Recovered"]).completeEvents,
]
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Continue" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
yield* session.resume(sessionID)
expect(requests).toHaveLength(3)
@ -1232,7 +1231,7 @@ describe("SessionRunnerLLM", () => {
[LLMEvent.providerError({ message: "prompt too long", classification: "context-overflow" })],
[LLMEvent.providerError({ message: "summary unavailable" })],
]
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Continue" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
yield* session.resume(sessionID)
expect(requests).toHaveLength(2)
@ -1255,7 +1254,7 @@ describe("SessionRunnerLLM", () => {
const firstGate = yield* Deferred.make<void>()
const summaryGate = yield* Deferred.make<void>()
streamGate = firstGate
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Continue" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
const run = yield* session.resume(sessionID).pipe(Effect.forkChild)
while (requests.length < 1) yield* Effect.yieldNow
streamGate = summaryGate
@ -1275,13 +1274,13 @@ describe("SessionRunnerLLM", () => {
yield* setup
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
requests.length = 0
response = []
yield* session.resume(sessionID)
systemBaseline = "Changed context"
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second" }), resume: false })
yield* session.resume(sessionID)
const compactionID = SessionMessage.ID.create()
yield* events.publish(SessionEvent.Compaction.Started, {
@ -1299,7 +1298,7 @@ describe("SessionRunnerLLM", () => {
recent: "",
})
systemUnavailable = true
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Third" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Third" }), resume: false })
yield* session.resume(sessionID)
expect(requests.at(-1)?.system.map((part) => part.text)).toEqual(["Initial context"])
@ -1311,7 +1310,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Use tools" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Use tools" }), resume: false })
requests.length = 0
responses = undefined
@ -1409,7 +1408,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Echo this" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Echo this" }), resume: false })
requests.length = 0
authorizations.length = 0
@ -1468,7 +1467,7 @@ describe("SessionRunnerLLM", () => {
yield* setup
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Echo this" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Echo this" }), resume: false })
requests.length = 0
responses = [
@ -1512,7 +1511,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Think first" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Think first" }), resume: false })
requests.length = 0
response = [
@ -1550,7 +1549,7 @@ describe("SessionRunnerLLM", () => {
},
])
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Continue" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
response = []
yield* session.resume(sessionID)
@ -1569,7 +1568,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Search first" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Search first" }), resume: false })
requests.length = 0
response = [
@ -1594,7 +1593,7 @@ describe("SessionRunnerLLM", () => {
yield* session.resume(sessionID)
yield* replaySessionProjection(sessionID)
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Continue" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
response = []
yield* session.resume(sessionID)
@ -1624,7 +1623,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Echo five times" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Echo five times" }), resume: false })
requests.length = 0
executions.length = 0
@ -1685,7 +1684,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Echo twice" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Echo twice" }), resume: false })
requests.length = 0
executions.length = 0
@ -1773,7 +1772,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Run once" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Run once" }), resume: false })
requests.length = 0
responses = undefined
@ -1812,7 +1811,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Start working" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Start working" }), resume: false })
requests.length = 0
responses = [
@ -1832,7 +1831,7 @@ describe("SessionRunnerLLM", () => {
const first = yield* session.resume(sessionID).pipe(Effect.forkChild)
yield* Deferred.await(streamStarted)
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Change direction" }) })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Change direction" }) })
yield* Deferred.succeed(streamGate, undefined)
yield* Fiber.join(first)
streamGate = undefined
@ -1855,7 +1854,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Start working" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Start working" }), resume: false })
requests.length = 0
responses = [
@ -1883,7 +1882,7 @@ describe("SessionRunnerLLM", () => {
yield* Deferred.await(streamStarted)
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Wait until continuation ends" }),
prompt: Prompt.make({ text: "Wait until continuation ends" }),
delivery: "queue",
})
yield* Deferred.succeed(streamGate, undefined)
@ -1903,7 +1902,7 @@ describe("SessionRunnerLLM", () => {
yield* setup
const session = yield* SessionV2.Service
const { db } = yield* Database.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Interrupt current work" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Interrupt current work" }), resume: false })
requests.length = 0
responses = [
@ -1921,7 +1920,7 @@ describe("SessionRunnerLLM", () => {
yield* Deferred.await(streamStarted)
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Run after interrupt" }),
prompt: Prompt.make({ text: "Run after interrupt" }),
delivery: "queue",
})
yield* session.interrupt(sessionID)
@ -1946,7 +1945,7 @@ describe("SessionRunnerLLM", () => {
yield* setup
const session = yield* SessionV2.Service
const { db } = yield* Database.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Interrupt current work" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Interrupt current work" }), resume: false })
requests.length = 0
responses = [
@ -1964,7 +1963,7 @@ describe("SessionRunnerLLM", () => {
yield* Deferred.await(streamStarted)
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Steer after interrupt" }),
prompt: Prompt.make({ text: "Steer after interrupt" }),
})
yield* session.interrupt(sessionID)
expect(yield* Fiber.await(run)).toMatchObject({ _tag: "Failure" })
@ -1988,7 +1987,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Start working" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Start working" }), resume: false })
requests.length = 0
responses = [
@ -2013,8 +2012,8 @@ describe("SessionRunnerLLM", () => {
const first = yield* session.resume(sessionID).pipe(Effect.forkChild)
yield* Deferred.await(streamStarted)
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Queue first" }), delivery: "queue" })
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Queue second" }), delivery: "queue" })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Queue first" }), delivery: "queue" })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Queue second" }), delivery: "queue" })
yield* Deferred.succeed(streamGate, undefined)
yield* Fiber.join(first)
streamGate = undefined
@ -2031,10 +2030,10 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Start steering" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Start steering" }), resume: false })
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Queue for later" }),
prompt: Prompt.make({ text: "Queue for later" }),
delivery: "queue",
resume: false,
})
@ -2065,7 +2064,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Start working" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Start working" }), resume: false })
requests.length = 0
responses = [
@ -2096,13 +2095,13 @@ describe("SessionRunnerLLM", () => {
const first = yield* session.resume(sessionID).pipe(Effect.forkChild)
while (requests.length < 1) yield* Effect.yieldNow
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Queue first" }), delivery: "queue" })
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Queue second" }), delivery: "queue" })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Queue first" }), delivery: "queue" })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Queue second" }), delivery: "queue" })
streamGate = secondGate
yield* Deferred.succeed(firstGate, undefined)
while (requests.length < 2) yield* Effect.yieldNow
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Steer before next queued input" }) })
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Also steer before next queued input" }) })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Steer before next queued input" }) })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Also steer before next queued input" }) })
yield* Deferred.succeed(secondGate, undefined)
yield* Fiber.join(first)
streamGate = undefined
@ -2130,7 +2129,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Start working" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Start working" }), resume: false })
requests.length = 0
responses = [
@ -2150,8 +2149,8 @@ describe("SessionRunnerLLM", () => {
const first = yield* session.resume(sessionID).pipe(Effect.forkChild)
yield* Deferred.await(streamStarted)
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First steer" }) })
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second steer" }) })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First steer" }) })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Second steer" }) })
yield* Deferred.succeed(streamGate, undefined)
yield* Fiber.join(first)
streamGate = undefined
@ -2170,7 +2169,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Start working" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Start working" }), resume: false })
requests.length = 0
responses = undefined
@ -2181,7 +2180,7 @@ describe("SessionRunnerLLM", () => {
const first = yield* session.resume(sessionID).pipe(Effect.forkChild)
yield* Deferred.await(streamStarted)
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Recover with this" }) })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Recover with this" }) })
yield* Deferred.succeed(streamGate, undefined)
expect(yield* Fiber.join(first).pipe(Effect.flip)).toBe(streamFailure)
@ -2200,7 +2199,7 @@ describe("SessionRunnerLLM", () => {
yield* setup
const session = yield* SessionV2.Service
const events = yield* EventV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Recover interrupted tool" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Recover interrupted tool" }), resume: false })
yield* SessionInput.promoteSteers((yield* Database.Service).db, events, sessionID, Number.MAX_SAFE_INTEGER)
const assistantMessageID = SessionMessage.ID.create()
yield* events.publish(SessionEvent.Step.Started, {
@ -2262,7 +2261,7 @@ describe("SessionRunnerLLM", () => {
const events = yield* EventV2.Service
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Recover interrupted hosted tool" }),
prompt: Prompt.make({ text: "Recover interrupted hosted tool" }),
resume: false,
})
yield* SessionInput.promoteSteers((yield* Database.Service).db, events, sessionID, Number.MAX_SAFE_INTEGER)
@ -2322,7 +2321,7 @@ describe("SessionRunnerLLM", () => {
const events = yield* EventV2.Service
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Recover interrupted tool input" }),
prompt: Prompt.make({ text: "Recover interrupted tool input" }),
resume: false,
})
yield* SessionInput.promoteSteers((yield* Database.Service).db, events, sessionID, Number.MAX_SAFE_INTEGER)
@ -2360,7 +2359,7 @@ describe("SessionRunnerLLM", () => {
const session = yield* SessionV2.Service
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Wait in queue" }),
prompt: Prompt.make({ text: "Wait in queue" }),
delivery: "queue",
resume: false,
})
@ -2382,7 +2381,7 @@ describe("SessionRunnerLLM", () => {
const defect = new Error("fail after prompt promotion")
let fail = true
yield* events.project(SessionEvent.Prompted, () => (fail ? Effect.die(defect) : Effect.void))
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Recover promoted input" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Recover promoted input" }), resume: false })
expect(yield* session.resume(sessionID).pipe(Effect.catchDefect(Effect.succeed))).toBe(defect)
fail = false
@ -2410,7 +2409,7 @@ describe("SessionRunnerLLM", () => {
)
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Run committed promotion" }),
prompt: Prompt.make({ text: "Run committed promotion" }),
resume: false,
})
@ -2427,8 +2426,8 @@ describe("SessionRunnerLLM", () => {
yield* setup
yield* insertSession(otherSessionID)
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Run first" }), resume: false })
yield* session.prompt({ sessionID: otherSessionID, prompt: new Prompt({ text: "Run second" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Run first" }), resume: false })
yield* session.prompt({ sessionID: otherSessionID, prompt: Prompt.make({ text: "Run second" }), resume: false })
requests.length = 0
responses = undefined
@ -2454,37 +2453,31 @@ describe("SessionRunnerLLM", () => {
}),
)
it.effect("bounds external session prompt cache keys", () =>
it.effect("bounds 64-character session prompt cache keys", () =>
Effect.gen(function* () {
yield* setup
const externalSessionID = SessionV2.ID.fromExternal({
namespace: "discord",
key: "thread-one",
})
const otherExternalSessionID = SessionV2.ID.fromExternal({
namespace: "discord",
key: "thread-two",
})
yield* insertSession(externalSessionID)
yield* insertSession(otherExternalSessionID)
const longSessionID = SessionV2.ID.make(`ses_${"a".repeat(64)}`)
const otherLongSessionID = SessionV2.ID.make(`ses_${"b".repeat(64)}`)
yield* insertSession(longSessionID)
yield* insertSession(otherLongSessionID)
const session = yield* SessionV2.Service
yield* session.prompt({
sessionID: externalSessionID,
prompt: new Prompt({ text: "Run external session" }),
sessionID: longSessionID,
prompt: Prompt.make({ text: "Run long session" }),
resume: false,
})
yield* session.prompt({
sessionID: otherExternalSessionID,
prompt: new Prompt({ text: "Run other external session" }),
sessionID: otherLongSessionID,
prompt: Prompt.make({ text: "Run other long session" }),
resume: false,
})
requests.length = 0
yield* session.resume(externalSessionID)
yield* session.resume(otherExternalSessionID)
yield* session.resume(longSessionID)
yield* session.resume(otherLongSessionID)
const keys = requests.map((request) => request.providerOptions?.openai?.promptCacheKey)
expect(keys).toEqual([externalSessionID.slice(4), otherExternalSessionID.slice(4)])
expect(keys).toEqual([longSessionID.slice(4), otherLongSessionID.slice(4)])
expect(keys.every((key) => typeof key === "string" && key.length === 64)).toBe(true)
expect(keys[0]).not.toBe(keys[1])
}),
@ -2494,7 +2487,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Retry after failure" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Retry after failure" }), resume: false })
requests.length = 0
responses = undefined
@ -2525,7 +2518,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Call missing" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Call missing" }), resume: false })
requests.length = 0
responses = [
@ -2571,7 +2564,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Call defect" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Call defect" }), resume: false })
requests.length = 0
responses = [
@ -2620,7 +2613,7 @@ describe("SessionRunnerLLM", () => {
questions.ask({ sessionID: context.sessionID, questions: [] }).pipe(Effect.as({}), Effect.orDie),
}),
})
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Ask then stop" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Ask then stop" }), resume: false })
requests.length = 0
responses = [
@ -2665,7 +2658,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Settle before failing" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Settle before failing" }), resume: false })
const failure = providerUnavailable()
toolExecutionGate = yield* Deferred.make<void>()
responseStream = Stream.concat(
@ -2699,7 +2692,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Interrupt blocked tool" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Interrupt blocked tool" }), resume: false })
executions.length = 0
toolExecutionGate = yield* Deferred.make<void>()
responseStream = Stream.concat(
@ -2749,7 +2742,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Interrupt provider" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Interrupt provider" }), resume: false })
requests.length = 0
response = []
streamGate = yield* Deferred.make<void>()
@ -2772,7 +2765,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Interrupt tool settlement" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Interrupt tool settlement" }), resume: false })
executions.length = 0
toolExecutionGate = yield* Deferred.make<void>()
response = [
@ -2815,7 +2808,7 @@ describe("SessionRunnerLLM", () => {
}),
)
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Finish at the limit" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Finish at the limit" }), resume: false })
requests.length = 0
executions.length = 0
@ -2863,7 +2856,7 @@ describe("SessionRunnerLLM", () => {
}),
)
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Start work" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Start work" }), resume: false })
requests.length = 0
executions.length = 0
@ -2891,7 +2884,7 @@ describe("SessionRunnerLLM", () => {
const run = yield* session.resume(sessionID).pipe(Effect.forkChild)
yield* Deferred.await(streamStarted)
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Change direction" }) })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Change direction" }) })
yield* Deferred.succeed(streamGate, undefined)
yield* Fiber.join(run)
streamGate = undefined
@ -2909,7 +2902,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Fail durably" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Fail durably" }), resume: false })
requests.length = 0
responses = undefined
@ -2931,7 +2924,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Fail before step" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Fail before step" }), resume: false })
requests.length = 0
response = [LLMEvent.providerError({ message: "Provider unavailable" })]
@ -2950,7 +2943,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Fail after output" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Fail after output" }), resume: false })
requests.length = 0
response = [
@ -2979,7 +2972,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Fail raw stream durably" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Fail raw stream durably" }), resume: false })
const failure = providerUnavailable()
responseStream = Stream.fail(failure)
@ -2998,7 +2991,7 @@ describe("SessionRunnerLLM", () => {
const session = yield* SessionV2.Service
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Do not continue failed provider" }),
prompt: Prompt.make({ text: "Do not continue failed provider" }),
resume: false,
})
@ -3021,7 +3014,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Fail hosted tool durably" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Fail hosted tool durably" }), resume: false })
requests.length = 0
response = [
@ -3052,7 +3045,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Fail hosted tool at EOF" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Fail hosted tool at EOF" }), resume: false })
response = [
LLMEvent.stepStart({ index: 0 }),
LLMEvent.toolCall({
@ -3079,7 +3072,7 @@ describe("SessionRunnerLLM", () => {
const session = yield* SessionV2.Service
yield* session.prompt({
sessionID,
prompt: new Prompt({ text: "Fail hosted tool on raw failure" }),
prompt: Prompt.make({ text: "Fail hosted tool on raw failure" }),
resume: false,
})
const failure = providerUnavailable()
@ -3114,7 +3107,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Two blocks" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Two blocks" }), resume: false })
responses = undefined
streamGate = undefined
@ -3177,7 +3170,7 @@ describe("SessionRunnerLLM", () => {
Effect.gen(function* () {
yield* setup
const session = yield* SessionV2.Service
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Call provider tool" }), resume: false })
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Call provider tool" }), resume: false })
responses = undefined
streamGate = undefined

View file

@ -0,0 +1,183 @@
import { expect, test } from "bun:test"
import { Schema } from "effect"
import { AgentV2 } from "@opencode-ai/core/agent"
import { ModelV2 } from "@opencode-ai/core/model"
import { SessionV2 } from "@opencode-ai/core/session"
import { Agent } from "@opencode-ai/schema/agent"
import { Location } from "@opencode-ai/schema/location"
import { Model } from "@opencode-ai/schema/model"
import { AgentAttachment, FileAttachment, Prompt, Source } from "@opencode-ai/schema/prompt"
import { Provider } from "@opencode-ai/schema/provider"
import { Project } from "@opencode-ai/schema/project"
import { Session } from "@opencode-ai/schema/session"
import { SessionInput } from "@opencode-ai/schema/session-input"
import { SessionMessage } from "@opencode-ai/schema/session-message"
import { Workspace } from "@opencode-ai/schema/workspace"
import { Command } from "@opencode-ai/schema/command"
import { Connection } from "@opencode-ai/schema/connection"
import { Credential } from "@opencode-ai/schema/credential"
import { FileSystem } from "@opencode-ai/schema/filesystem"
import { Integration } from "@opencode-ai/schema/integration"
import { LLM } from "@opencode-ai/schema/llm"
import { ModelRequest } from "@opencode-ai/schema/model-request"
import { Permission } from "@opencode-ai/schema/permission"
import { Reference } from "@opencode-ai/schema/reference"
import { Skill } from "@opencode-ai/schema/skill"
import { AbsolutePath, DateTimeUtcFromMillis } from "@opencode-ai/schema/schema"
import { ProviderV2 } from "@opencode-ai/core/provider"
test("Core reuses the canonical shared schemas", async () => {
const [
coreCommand,
coreConnection,
coreCredential,
coreFileSystem,
coreIntegration,
coreLocation,
coreLLM,
coreModelRequest,
corePermission,
coreProject,
coreReference,
coreSessionInput,
coreSessionMessage,
corePrompt,
coreSkill,
coreV2Schema,
coreWorkspace,
] = await Promise.all([
import("@opencode-ai/core/command"),
import("@opencode-ai/core/integration/connection"),
import("@opencode-ai/core/credential"),
import("@opencode-ai/core/filesystem/schema"),
import("@opencode-ai/core/integration"),
import("@opencode-ai/core/location"),
import("@opencode-ai/llm"),
import("@opencode-ai/core/model-request"),
import("@opencode-ai/core/permission/schema"),
import("@opencode-ai/core/project/schema"),
import("@opencode-ai/core/reference"),
import("@opencode-ai/core/session/input"),
import("@opencode-ai/core/session/message"),
import("@opencode-ai/core/session/prompt"),
import("@opencode-ai/core/skill"),
import("@opencode-ai/core/v2-schema"),
import("@opencode-ai/core/workspace"),
])
const schemas = [
[AgentV2.ID, Agent.ID],
[AgentV2.Color, Agent.Color],
[AgentV2.Info, Agent.Info],
[coreCommand.Info, Command.Info],
[coreConnection.CredentialInfo, Connection.CredentialInfo],
[coreConnection.EnvInfo, Connection.EnvInfo],
[coreConnection.Info, Connection.Info],
[coreCredential.ID, Credential.ID],
[coreCredential.OAuth, Credential.OAuth],
[coreCredential.Key, Credential.Key],
[coreCredential.Value, Credential.Value],
[coreFileSystem.Entry, FileSystem.Entry],
[coreFileSystem.Submatch, FileSystem.Submatch],
[coreFileSystem.Match, FileSystem.Match],
[coreIntegration.When, Integration.When],
[coreIntegration.TextPrompt, Integration.TextPrompt],
[coreIntegration.SelectPrompt, Integration.SelectPrompt],
[coreIntegration.Prompt, Integration.Prompt],
[coreIntegration.OAuthMethod, Integration.OAuthMethod],
[coreIntegration.KeyMethod, Integration.KeyMethod],
[coreIntegration.EnvMethod, Integration.EnvMethod],
[coreIntegration.Method, Integration.Method],
[coreIntegration.Inputs, Integration.Inputs],
[coreIntegration.Ref, Integration.Ref],
[coreLocation.Ref, Location.Ref],
[coreLLM.ProviderMetadata, LLM.ProviderMetadata],
[coreLLM.ToolTextContent, LLM.ToolTextContent],
[coreLLM.ToolFileContent, LLM.ToolFileContent],
[coreLLM.ToolContent, LLM.ToolContent],
[ModelV2.ID, Model.ID],
[ModelV2.VariantID, Model.VariantID],
[ModelV2.Ref, Model.Ref],
[ModelV2.Family, Model.Family],
[ModelV2.Capabilities, Model.Capabilities],
[ModelV2.Cost, Model.Cost],
[ModelV2.Api, Model.Api],
[ModelV2.Info, Model.Info],
[ProviderV2.ID, Provider.ID],
[ProviderV2.AISDK, Provider.AISDK],
[ProviderV2.Native, Provider.Native],
[ProviderV2.Api, Provider.Api],
[ProviderV2.Request, Provider.Request],
[ProviderV2.Info, Provider.Info],
[coreModelRequest.Generation, ModelRequest.Generation],
[coreModelRequest.Request, ModelRequest.Request],
[corePermission.Effect, Permission.Effect],
[corePermission.Rule, Permission.Rule],
[corePermission.Ruleset, Permission.Ruleset],
[coreProject.ID, Project.ID],
[coreReference.LocalSource, Reference.LocalSource],
[coreReference.GitSource, Reference.GitSource],
[coreReference.Source, Reference.Source],
[SessionV2.ID, Session.ID],
[SessionV2.Info, Session.Info],
[SessionV2.ListAnchor, Session.ListAnchor],
[coreSessionInput.Delivery, SessionInput.Delivery],
[coreSessionInput.Admitted, SessionInput.Admitted],
[coreSessionMessage.ID, SessionMessage.ID],
[coreSessionMessage.UnknownError, SessionMessage.UnknownError],
[coreSessionMessage.AgentSwitched, SessionMessage.AgentSwitched],
[coreSessionMessage.ModelSwitched, SessionMessage.ModelSwitched],
[coreSessionMessage.User, SessionMessage.User],
[coreSessionMessage.Synthetic, SessionMessage.Synthetic],
[coreSessionMessage.System, SessionMessage.System],
[coreSessionMessage.Shell, SessionMessage.Shell],
[coreSessionMessage.ToolStatePending, SessionMessage.ToolStatePending],
[coreSessionMessage.ToolStateRunning, SessionMessage.ToolStateRunning],
[coreSessionMessage.ToolStateCompleted, SessionMessage.ToolStateCompleted],
[coreSessionMessage.ToolStateError, SessionMessage.ToolStateError],
[coreSessionMessage.ToolState, SessionMessage.ToolState],
[coreSessionMessage.AssistantTool, SessionMessage.AssistantTool],
[coreSessionMessage.AssistantText, SessionMessage.AssistantText],
[coreSessionMessage.AssistantReasoning, SessionMessage.AssistantReasoning],
[coreSessionMessage.AssistantContent, SessionMessage.AssistantContent],
[coreSessionMessage.Assistant, SessionMessage.Assistant],
[coreSessionMessage.Compaction, SessionMessage.Compaction],
[coreSessionMessage.Message, SessionMessage.Message],
[corePrompt.Source, Source],
[corePrompt.FileAttachment, FileAttachment],
[corePrompt.AgentAttachment, AgentAttachment],
[corePrompt.Prompt, Prompt],
[coreSkill.DirectorySource, Skill.DirectorySource],
[coreSkill.UrlSource, Skill.UrlSource],
[coreSkill.EmbeddedSource, Skill.EmbeddedSource],
[coreSkill.Source, Skill.Source],
[coreSkill.Info, Skill.Info],
[coreV2Schema.DateTimeUtcFromMillis, DateTimeUtcFromMillis],
[coreWorkspace.ID, Workspace.ID],
]
for (const [core, shared] of schemas) expect(core).toBe(shared)
expect(Agent.Info.empty(Agent.ID.make("test"))).toEqual(AgentV2.Info.empty(AgentV2.ID.make("test")))
expect(Model.Info.empty(Provider.ID.make("test"), Model.ID.make("model"))).toEqual(
ModelV2.Info.empty(ProviderV2.ID.make("test"), ModelV2.ID.make("model")),
)
expect(Provider.Info.empty(Provider.ID.make("test"))).toEqual(ProviderV2.Info.empty(ProviderV2.ID.make("test")))
expect(Skill.Source.key(Skill.DirectorySource.make({ type: "directory", path: AbsolutePath.make("/tmp") }))).toBe(
"directory:/tmp",
)
})
test("shared record schemas construct and decode plain objects", () => {
const made = Prompt.make({ text: "hello" })
const decoded = Schema.decodeUnknownSync(Prompt)({ text: "hello" })
const content = Schema.decodeUnknownSync(SessionMessage.AssistantText)({ type: "text", id: "part_1", text: "hi" })
expect(Object.getPrototypeOf(made)).toBe(Object.prototype)
expect(Object.getPrototypeOf(decoded)).toBe(Object.prototype)
expect(Object.getPrototypeOf(content)).toBe(Object.prototype)
expect(Prompt.ast.annotations?.identifier).toBe("Prompt")
expect(SessionMessage.AssistantText.ast.annotations?.identifier).toBe("Session.Message.Assistant.Text")
expect(Prompt.equivalence(Prompt.make({ text: "hello" }), decoded)).toBe(true)
expect(Prompt.fromUserMessage({ text: "hello" })).toEqual(made)
expect(Workspace.ID.ascending("")).toStartWith("wrk_")
})

View file

@ -74,7 +74,7 @@ describe("SkillV2", () => {
{ type: "directory", path: AbsolutePath.make(second) },
])
expect(yield* skill.list()).toEqual([
new SkillV2.Info({
SkillV2.Info.make({
name: "foo",
slash: true,
location: AbsolutePath.make(path.join(first, "foo.md")),

View file

@ -9,18 +9,18 @@ import { SkillGuidance } from "@opencode-ai/core/skill/guidance"
import { it } from "../lib/effect"
const build = AgentV2.ID.make("build")
const effect = new SkillV2.Info({
const effect = SkillV2.Info.make({
name: "effect",
description: "Build applications with Effect",
location: AbsolutePath.make(path.resolve("/skills/effect/SKILL.md")),
content: "Effect guidance",
})
const hidden = new SkillV2.Info({
const hidden = SkillV2.Info.make({
name: "hidden",
location: AbsolutePath.make(path.resolve("/skills/hidden/SKILL.md")),
content: "Undescribed guidance",
})
const denied = new SkillV2.Info({
const denied = SkillV2.Info.make({
name: "denied",
description: "Must not be advertised",
location: AbsolutePath.make(path.resolve("/skills/denied/SKILL.md")),
@ -32,7 +32,7 @@ const layer = (list: () => SkillV2.Info[]) =>
describe("SkillGuidance", () => {
it.effect("renders described agent skills and reconciles the complete available list", () => {
const agent = new AgentV2.Info({
const agent = AgentV2.Info.make({
...AgentV2.Info.empty(build),
permissions: [{ action: "skill", resource: "denied", effect: "deny" }],
})
@ -69,7 +69,7 @@ describe("SkillGuidance", () => {
})
it.effect("omits guidance when the selected agent denies all skills", () => {
const agent = new AgentV2.Info({
const agent = AgentV2.Info.make({
...AgentV2.Info.empty(build),
permissions: [{ action: "skill", resource: "*", effect: "deny" }],
})
@ -85,7 +85,7 @@ describe("SkillGuidance", () => {
})
it.effect("omits guidance when a resource-specific denial follows the global denial", () => {
const agent = new AgentV2.Info({
const agent = AgentV2.Info.make({
...AgentV2.Info.empty(build),
permissions: [
{ action: "skill", resource: "*", effect: "deny" },
@ -104,7 +104,7 @@ describe("SkillGuidance", () => {
})
it.effect("retains specifically allowed skills after a global denial", () => {
const agent = new AgentV2.Info({
const agent = AgentV2.Info.make({
...AgentV2.Info.empty(build),
permissions: [
{ action: "skill", resource: "*", effect: "deny" },
@ -120,7 +120,7 @@ describe("SkillGuidance", () => {
})
it.effect("omits guidance when a specifically allowed skill is denied again", () => {
const agent = new AgentV2.Info({
const agent = AgentV2.Info.make({
...AgentV2.Info.empty(build),
permissions: [
{ action: "skill", resource: "*", effect: "deny" },

View file

@ -122,7 +122,7 @@ describe("SkillTool", () => {
}),
).toEqual({ type: "error", value: "Unable to load skill effect" })
deny = false
const flat = new SkillV2.Info({
const flat = SkillV2.Info.make({
name: "public",
description: "Public guidance",
location: AbsolutePath.make(path.join(tmp.path, "public.md")),