| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- import { expect, test } from "bun:test"
- import type { AgentSideConnection } from "@agentclientprotocol/sdk"
- import { OpenCode } from "@opencode-ai/client/promise"
- import { streamTurn } from "../../src/acp/event"
- test("acp prompt resolves after ordered turn updates", async () => {
- const encoder = new TextEncoder()
- let events: ReadableStreamDefaultController<Uint8Array> | undefined
- const updates: Parameters<AgentSideConnection["sessionUpdate"]>[0][] = []
- const server = Bun.serve({
- port: 0,
- async fetch(request) {
- const url = new URL(request.url)
- if (url.pathname === "/api/event") {
- return new Response(
- new ReadableStream<Uint8Array>({
- start(controller) {
- events = controller
- send(controller, { id: "evt_connected", type: "server.connected", data: {} })
- },
- }),
- { headers: { "content-type": "text/event-stream" } },
- )
- }
- if (url.pathname === "/api/session/ses_test/prompt") {
- const body: unknown = await request.json()
- if (!body || typeof body !== "object") {
- return new Response(null, { status: 400 })
- }
- const id = Reflect.get(body, "id")
- if (typeof id !== "string") return new Response(null, { status: 400 })
- queueMicrotask(() => {
- if (!events) return
- send(events, {
- id: "evt_promoted",
- created: 1,
- type: "session.input.promoted",
- data: { sessionID: "ses_test", inputID: id },
- })
- send(events, {
- id: "evt_text",
- created: 2,
- type: "session.text.delta",
- data: { sessionID: "ses_test", assistantMessageID: "msg_assistant", ordinal: 0, delta: "hello" },
- })
- send(events, {
- id: "evt_step",
- created: 3,
- type: "session.step.ended",
- data: {
- sessionID: "ses_test",
- assistantMessageID: "msg_assistant",
- finish: "stop",
- cost: 0,
- tokens: { input: 1, output: 1, reasoning: 0, cache: { read: 0, write: 0 } },
- },
- })
- send(events, {
- id: "evt_done",
- created: 4,
- type: "session.execution.succeeded",
- data: { sessionID: "ses_test" },
- })
- })
- return Response.json({ data: {} })
- }
- if (url.pathname === "/api/session/ses_test/message/msg_assistant") {
- return Response.json({
- data: {
- id: "msg_assistant",
- type: "assistant",
- agent: "build",
- model: { providerID: "test", id: "test-model" },
- content: [{ type: "text", text: "hello" }],
- finish: "stop",
- tokens: { input: 1, output: 1, reasoning: 0, cache: { read: 0, write: 0 } },
- time: { created: 1, completed: 4 },
- },
- })
- }
- return new Response(null, { status: 404 })
- },
- })
- const client = OpenCode.make({ baseUrl: server.url.toString() })
- try {
- const id = "msg_prompt"
- const response = await streamTurn({
- client,
- connection: {
- sessionUpdate: async (update) => {
- updates.push(update)
- },
- requestPermission: async () => ({ outcome: { outcome: "cancelled" } }),
- },
- sessionID: "ses_test",
- cwd: "/workspace",
- start: { type: "input", id },
- writeTextFile: false,
- control: { cancelled: false, admission: new AbortController() },
- submit: () => client.session.prompt({ sessionID: "ses_test", id, text: "hi" }),
- })
- expect(updates).toEqual([
- {
- sessionId: "ses_test",
- update: {
- sessionUpdate: "agent_message_chunk",
- messageId: "msg_assistant",
- content: { type: "text", text: "hello" },
- },
- },
- ])
- expect(response).toMatchObject({ stopReason: "end_turn", usage: { totalTokens: 2 } })
- } finally {
- events?.close()
- await server.stop(true)
- }
- function send(controller: ReadableStreamDefaultController<Uint8Array>, event: unknown) {
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`))
- }
- })
|