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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { convertToOpenAICompatibleChatMessages as convertToCopilotMessages } from "@opencode-ai/core/github-copilot/chat/convert-to-openai-compatible-chat-messages"
import { convertToOpenAICompatibleChatMessages } from "@opencode-ai/core/github-copilot/chat/convert-to-openai-compatible-chat-messages"
import { describe, test, expect } from "bun:test"

describe("system messages", () => {
test("should convert system message content to string", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "system",
content: "You are a helpful assistant with AGENTS.md instructions.",
Expand All @@ -21,7 +21,7 @@ describe("system messages", () => {

describe("user messages", () => {
test("should convert messages with only a text part to a string content", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "user",
content: [{ type: "text", text: "Hello" }],
Expand All @@ -32,7 +32,7 @@ describe("user messages", () => {
})

test("should convert messages with image parts", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "user",
content: [
Expand Down Expand Up @@ -61,7 +61,7 @@ describe("user messages", () => {
})

test("should convert messages with image parts from Uint8Array", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "user",
content: [
Expand Down Expand Up @@ -90,7 +90,7 @@ describe("user messages", () => {
})

test("should handle URL-based images", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "user",
content: [
Expand All @@ -117,7 +117,7 @@ describe("user messages", () => {
})

test("should handle multiple text parts without flattening", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "user",
content: [
Expand All @@ -141,7 +141,7 @@ describe("user messages", () => {

describe("assistant messages", () => {
test("should convert assistant text messages", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "assistant",
content: [{ type: "text", text: "Hello back!" }],
Expand All @@ -160,7 +160,7 @@ describe("assistant messages", () => {
})

test("should handle assistant message with null content when only tool calls", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "assistant",
content: [
Expand Down Expand Up @@ -195,7 +195,7 @@ describe("assistant messages", () => {
})

test("should concatenate multiple text parts", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "assistant",
content: [
Expand All @@ -211,7 +211,7 @@ describe("assistant messages", () => {

describe("tool calls", () => {
test("should stringify arguments to tool calls", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "assistant",
content: [
Expand Down Expand Up @@ -262,7 +262,7 @@ describe("tool calls", () => {
})

test("should handle text output type in tool results", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "tool",
content: [
Expand All @@ -286,7 +286,7 @@ describe("tool calls", () => {
})

test("should handle multiple tool results as separate messages", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "tool",
content: [
Expand Down Expand Up @@ -320,7 +320,7 @@ describe("tool calls", () => {
})

test("should handle text plus multiple tool calls", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "assistant",
content: [
Expand Down Expand Up @@ -373,7 +373,7 @@ describe("tool calls", () => {

describe("reasoning (copilot-specific)", () => {
test("should omit reasoning_text without reasoning_opaque", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "assistant",
content: [
Expand All @@ -395,7 +395,7 @@ describe("reasoning (copilot-specific)", () => {
})

test("should include reasoning_opaque from providerOptions", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "assistant",
content: [
Expand Down Expand Up @@ -423,7 +423,7 @@ describe("reasoning (copilot-specific)", () => {
})

test("should include reasoning_opaque from text part providerOptions", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "assistant",
content: [
Expand All @@ -450,7 +450,7 @@ describe("reasoning (copilot-specific)", () => {
})

test("should handle reasoning-only assistant message", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "assistant",
content: [
Expand Down Expand Up @@ -479,7 +479,7 @@ describe("reasoning (copilot-specific)", () => {

describe("full conversation", () => {
test("should convert a multi-turn conversation with reasoning", () => {
const result = convertToCopilotMessages([
const result = convertToOpenAICompatibleChatMessages([
{
role: "system",
content: "You are a helpful assistant.",
Expand Down
2 changes: 0 additions & 2 deletions packages/core/test/plugin/provider-dynamic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ describe("DynamicProviderPlugin", () => {

itWithAISDK.live("wraps missing provider factory exports as AISDK init errors", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const tmp = yield* tempEntrypoint("export const notAProviderFactory = true\n")
yield* addPlugin(npmEntrypoint(tmp.entrypoint))
Expand All @@ -176,7 +175,6 @@ describe("DynamicProviderPlugin", () => {

itWithAISDK.effect("uses the model modelID for the default language model", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const language = yield* aisdk.language(
Expand Down
20 changes: 8 additions & 12 deletions packages/core/test/plugin/provider-github-copilot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const it = testEffect(PluginTestLayer)

const addPlugin = Effect.fn(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const host = yield* PluginHost.make(plugin)
yield* GithubCopilotPlugin.effect(host)
})
Expand Down Expand Up @@ -57,7 +56,8 @@ describe("GithubCopilotPlugin", () => {
it.effect("registers GitHub Copilot device OAuth", () =>
Effect.gen(function* () {
yield* addPlugin()
expect((yield* (yield* Integration.Service).get(Integration.ID.make("github-copilot")))?.methods).toContainEqual({
const integrations = yield* Integration.Service
expect((yield* integrations.get(Integration.ID.make("github-copilot")))?.methods).toContainEqual({
id: Integration.MethodID.make("device"),
type: "oauth",
label: "Login with GitHub Copilot",
Expand Down Expand Up @@ -124,7 +124,8 @@ describe("GithubCopilotPlugin", () => {
it.effect("adds Copilot authentication to native Anthropic requests", () =>
Effect.gen(function* () {
yield* addPlugin()
const event = yield* (yield* PluginHooks.Service).trigger("session", "http.request", {
const hooks = yield* PluginHooks.Service
const event = yield* hooks.trigger("session", "http.request", {
sessionID: Session.ID.make("ses_test"),
agent: Agent.ID.make("build"),
model: Model.Ref.make({ providerID: Provider.ID.githubCopilot, id: Model.ID.make("claude-sonnet-4.5") }),
Expand All @@ -145,7 +146,8 @@ describe("GithubCopilotPlugin", () => {
it.effect("classifies title generation as a background interaction", () =>
Effect.gen(function* () {
yield* addPlugin()
const event = yield* (yield* PluginHooks.Service).trigger("session", "http.request", {
const hooks = yield* PluginHooks.Service
const event = yield* hooks.trigger("session", "http.request", {
sessionID: Session.ID.make("ses_title"),
agent: Agent.ID.make("title"),
model: Model.Ref.make({ providerID: Provider.ID.githubCopilot, id: Model.ID.make("gpt-5.4-nano") }),
Expand All @@ -158,7 +160,8 @@ describe("GithubCopilotPlugin", () => {
it.effect("classifies compaction requests", () =>
Effect.gen(function* () {
yield* addPlugin()
const event = yield* (yield* PluginHooks.Service).trigger("session", "http.request", {
const hooks = yield* PluginHooks.Service
const event = yield* hooks.trigger("session", "http.request", {
sessionID: Session.ID.make("ses_compaction"),
agent: Agent.ID.make("compaction"),
model: Model.Ref.make({ providerID: Provider.ID.githubCopilot, id: Model.ID.make("gpt-5.4") }),
Expand All @@ -170,7 +173,6 @@ describe("GithubCopilotPlugin", () => {

it.effect("creates the bundled Copilot SDK for the GitHub Copilot package", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
yield* addPlugin()
const ignored = yield* aisdk.runSDK({
Expand Down Expand Up @@ -221,7 +223,6 @@ describe("GithubCopilotPlugin", () => {

it.effect("selects languageModel when responses and chat are absent", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: string[] = []
yield* addPlugin()
Expand All @@ -240,7 +241,6 @@ describe("GithubCopilotPlugin", () => {

it.effect("selects languageModel with the API model ID when responses and chat are absent", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: string[] = []
yield* addPlugin()
Expand All @@ -259,7 +259,6 @@ describe("GithubCopilotPlugin", () => {

it.effect("uses responses for gpt-5 models except gpt-5-mini", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: string[] = []
yield* addPlugin()
Expand Down Expand Up @@ -320,7 +319,6 @@ describe("GithubCopilotPlugin", () => {

it.effect("uses advertised Copilot endpoint metadata before model ID fallbacks", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: string[] = []
yield* addPlugin()
Expand Down Expand Up @@ -350,7 +348,6 @@ describe("GithubCopilotPlugin", () => {

it.effect("uses the API model ID when selecting responses or chat", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: string[] = []
yield* addPlugin()
Expand Down Expand Up @@ -417,7 +414,6 @@ describe("GithubCopilotPlugin", () => {

it.effect("ignores non-Copilot providers", () =>
Effect.gen(function* () {
const plugin = yield* Plugin.Service
const aisdk = yield* AISDK.Service
const calls: string[] = []
yield* addPlugin()
Expand Down
8 changes: 4 additions & 4 deletions packages/core/test/plugin/provider-kilo.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect } from "bun:test"
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog"
import { Plugin } from "@opencode-ai/core/plugin"
Expand All @@ -18,9 +18,9 @@ const addPlugin = Effect.fn(function* () {
})

describe("KiloPlugin", () => {
it.effect("is registered so legacy referer headers can be applied", () =>
Effect.sync(() => expect(ProviderPlugins.map((item) => item.id)).toContain("opencode.provider.kilo")),
)
test("is registered so legacy referer headers can be applied", () => {
expect(ProviderPlugins.map((item) => item.id)).toContain("opencode.provider.kilo")
})

it.effect("applies legacy referer headers only to Kilo endpoints", () =>
Effect.gen(function* () {
Expand Down
11 changes: 5 additions & 6 deletions packages/core/test/plugin/provider-llmgateway.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect } from "bun:test"
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog"
import { Integration } from "@opencode-ai/core/integration"
Expand All @@ -15,14 +15,13 @@ const it = testEffect(PluginTestLayer)
const addPlugin = Effect.fn(function* () {
const plugin = yield* Plugin.Service
const host = yield* PluginHost.make(plugin)
const integration = yield* Integration.Service
yield* LLMGatewayPlugin.effect(host).pipe(Effect.provideService(Integration.Service, integration))
yield* LLMGatewayPlugin.effect(host)
})

describe("LLMGatewayPlugin", () => {
it.effect("is registered so legacy referer headers can be applied", () =>
Effect.sync(() => expect(ProviderPlugins.map((item) => item.id)).toContain("opencode.provider.llmgateway")),
)
test("is registered so legacy referer headers can be applied", () => {
expect(ProviderPlugins.map((item) => item.id)).toContain("opencode.provider.llmgateway")
})

it.effect("applies legacy referer headers only to enabled llmgateway", () =>
Effect.gen(function* () {
Expand Down
12 changes: 5 additions & 7 deletions packages/core/test/plugin/provider-lmstudio.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { LMStudioPlugin, make } from "@opencode-ai/core/plugin/provider/lmstudio
import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
import { Provider } from "@opencode-ai/core/provider"
import { Document, Event, Info } from "@opencode-ai/schema/config"
import { describe, expect } from "bun:test"
import { describe, expect, test } from "bun:test"
import { Duration, Effect, Layer, Schema } from "effect"
import { testEffect } from "../lib/effect"
import { PluginTestLayer } from "./fixture"
Expand Down Expand Up @@ -38,12 +38,10 @@ function eventually<A>(
}

describe("LMStudioPlugin", () => {
it.effect("is registered as a built-in provider plugin", () =>
Effect.sync(() => {
expect(LMStudioPlugin.id).toBe("opencode.provider.lmstudio")
expect(ProviderPlugins.map((item) => item.id)).toContain("opencode.provider.lmstudio")
}),
)
test("is registered as a built-in provider plugin", () => {
expect(LMStudioPlugin.id).toBe("opencode.provider.lmstudio")
expect(ProviderPlugins.map((item) => item.id)).toContain("opencode.provider.lmstudio")
})

it.live("discovers local language models with their capabilities and effective context", () =>
Effect.acquireUseRelease(
Expand Down
8 changes: 4 additions & 4 deletions packages/core/test/plugin/provider-nvidia.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect } from "bun:test"
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { Catalog } from "@opencode-ai/core/catalog"
import { Plugin } from "@opencode-ai/core/plugin"
Expand All @@ -18,9 +18,9 @@ const addPlugin = Effect.fn(function* () {
})

describe("NvidiaPlugin", () => {
it.effect("is registered so legacy referer headers can be applied", () =>
Effect.sync(() => expect(ProviderPlugins.map((item) => item.id)).toContain("opencode.provider.nvidia")),
)
test("is registered so legacy referer headers can be applied", () => {
expect(ProviderPlugins.map((item) => item.id)).toContain("opencode.provider.nvidia")
})

it.effect("applies NVIDIA tracking headers only to nvidia", () =>
Effect.gen(function* () {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/test/plugin/provider-openai.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ const it = testEffect(PluginTestLayer)
const addPlugin = Effect.fn(function* () {
const plugin = yield* Plugin.Service
const host = yield* PluginHost.make(plugin)
const integrations = yield* Integration.Service
yield* OpenAIPlugin.effect(host).pipe(Effect.provideService(Integration.Service, integrations))
yield* OpenAIPlugin.effect(host)
})

const addGithubCopilotPlugin = Effect.fn(function* () {
Expand Down Expand Up @@ -65,7 +64,8 @@ describe("OpenAIPlugin", () => {
it.effect("registers browser and headless ChatGPT OAuth methods", () =>
Effect.gen(function* () {
yield* addPlugin()
expect((yield* (yield* Integration.Service).get(Integration.ID.make("openai")))?.methods).toEqual([
const integrations = yield* Integration.Service
expect((yield* integrations.get(Integration.ID.make("openai")))?.methods).toEqual([
{
id: Integration.MethodID.make("chatgpt-browser"),
type: "oauth",
Expand Down
Loading
Loading