openAgent
gitlabThe open source UNIVERSAL agent.
clone
https://gitlab.com/bitzzdev/openAgent.gitAIagentautomationopen-sourceuniversal-agent
import { expect, test } from "bun:test"import { isSessionNotFoundError, isUnauthorizedError, OpenCode } from "../src"test("exposes every standard HTTP API group", () => {const client = OpenCode.make({ baseUrl: "http://localhost:3000" })expect(Object.keys(client)).toEqual(["health","location","agents","sessions","messages","models","providers","integrations","credentials","permissions","files","commands","skills","events","ptys","questions","references","projectCopies",])expect(Object.keys(client.messages)).toEqual(["list"])expect(Object.keys(client.integrations)).toEqual(["list","get","connectKey","connectOauth","attemptStatus","attemptComplete","attemptCancel",])expect(Object.keys(client.files)).toEqual(["list", "find"])expect(Object.keys(client.ptys)).toEqual(["list", "create", "get", "update", "remove"])})test("sessions.get returns the wire projection", async () => {const client = OpenCode.make({baseUrl: "http://localhost:3000",fetch: async (input) => {expect(typeof input === "string" ? input : input instanceof URL ? input.href : input.url).toBe("http://localhost:3000/api/session/ses_test",)return Response.json(session)},})const result = await client.sessions.get({ sessionID: "ses_test" })expect(result.time.created).toBe(1_717_171_717_000)})test("events.subscribe exposes the Promise event stream wire projection", async () => {const client = OpenCode.make({baseUrl: "http://localhost:3000",fetch: async () =>new Response(`: heartbeat\n\ndata: ${JSON.stringify({ id: "evt_connected", type: "server.connected", data: {} })}\n\n` +`data: ${JSON.stringify(modelSwitchedEvent)}\n\n`,{ headers: { "content-type": "text/event-stream" } },),})const events = []for await (const event of client.events.subscribe()) events.push(event)expect(events).toEqual([{ id: "evt_connected", type: "server.connected", data: {} }, modelSwitchedEvent])expect(events[1]?.type === "session.next.model.switched" && events[1].data.timestamp).toBe(1_717_171_717_000)})test("events.subscribe terminates on malformed Promise SSE data", async () => {const client = OpenCode.make({baseUrl: "http://localhost:3000",fetch: async () => new Response("data: {not-json}\n\n", { headers: { "content-type": "text/event-stream" } }),})await expect(client.events.subscribe()[Symbol.asyncIterator]().next()).rejects.toMatchObject({name: "ClientError",reason: "MalformedResponse",})})test("session methods use the public HTTP contract", async () => {const requests: Array<{ url: string; init?: RequestInit }> = []let historyPage = 0const client = OpenCode.make({baseUrl: "http://localhost:3000",fetch: async (input, init) => {const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.urlrequests.push({ url, init })if (url.includes("/event")) {return new Response(`data: ${JSON.stringify(modelSwitchedEvent)}\n\n`, {headers: { "content-type": "text/event-stream" },})}if (url.includes("/history")) {historyPage++return Response.json(historyPage === 1 ? { data: [modelSwitchedEvent], hasMore: true } : { data: [], hasMore: false },)}if (url.includes("/prompt")) return Response.json(admission)if (url.includes("/context")) return Response.json({ data: [] })if (url.includes("/message/")) return Response.json({ data: modelSwitchedMessage })if (url.endsWith("/api/session/active")) return Response.json({ data: { ses_test: { type: "running" } } })if (init?.method === "POST" && url.endsWith("/api/session")) return Response.json(session)if (init?.method === "POST") return new Response(null, { status: 204 })return Response.json({ data: [session.data], cursor: { next: "next" } })},})const page = await client.sessions.list({ limit: 10, order: "desc" })const active = await client.sessions.active()const created = await client.sessions.create({ location: { directory: "/tmp/project" } })await client.sessions.switchAgent({ sessionID: "ses_test", agent: "build" })await client.sessions.switchModel({sessionID: "ses_test",model: { id: "claude", providerID: "anthropic" },})const admitted = await client.sessions.prompt({sessionID: "ses_test",prompt: { text: "Hello" },resume: false,})await client.sessions.compact({ sessionID: "ses_test" })await client.sessions.wait({ sessionID: "ses_test" })const context = await client.sessions.context({ sessionID: "ses_test" })const history = await client.sessions.history({ sessionID: "ses_test", after: 0, limit: 1 })const historyAfter = history.data.at(-1)?.durable?.seqconst historyNext = history.hasMore? await client.sessions.history({ sessionID: "ses_test", after: historyAfter, limit: 2 }): undefinedconst events = []for await (const event of client.sessions.events({ sessionID: "ses_test", after: 0 })) events.push(event)await client.sessions.interrupt({ sessionID: "ses_test" })const message = await client.sessions.message({ sessionID: "ses_test", messageID: "msg_model" })expect(page.cursor.next).toBe("next")expect(active).toEqual({ ses_test: { type: "running" } })expect(created.id).toBe("ses_test")expect(admitted.id).toBe("msg_test")expect(context).toEqual([])expect(history).toEqual({ data: [modelSwitchedEvent], hasMore: true })expect(historyNext).toEqual({ data: [], hasMore: false })expect(events).toEqual([modelSwitchedEvent])expect(message).toEqual(modelSwitchedMessage)expect(requests.map((request) => [request.init?.method, request.url])).toEqual([["GET", "http://localhost:3000/api/session?limit=10&order=desc"],["GET", "http://localhost:3000/api/session/active"],["POST", "http://localhost:3000/api/session"],["POST", "http://localhost:3000/api/session/ses_test/agent"],["POST", "http://localhost:3000/api/session/ses_test/model"],["POST", "http://localhost:3000/api/session/ses_test/prompt"],["POST", "http://localhost:3000/api/session/ses_test/compact"],["POST", "http://localhost:3000/api/session/ses_test/wait"],["GET", "http://localhost:3000/api/session/ses_test/context"],["GET", "http://localhost:3000/api/session/ses_test/history?limit=1&after=0"],["GET", "http://localhost:3000/api/session/ses_test/history?limit=2&after=1"],["GET", "http://localhost:3000/api/session/ses_test/event?after=0"],["POST", "http://localhost:3000/api/session/ses_test/interrupt"],["GET", "http://localhost:3000/api/session/ses_test/message/msg_model"],])const body = requests.find((request) => request.url.endsWith("/api/session/ses_test/prompt"))?.init?.bodyif (typeof body !== "string") throw new Error("Expected JSON request body")expect(JSON.parse(body)).toEqual({prompt: { text: "Hello" },resume: false,})})test("middleware errors remain declared client errors", async () => {const client = OpenCode.make({baseUrl: "http://localhost:3000",fetch: async () =>Response.json({ _tag: "UnauthorizedError", message: "Authentication required" }, { status: 401 }),})try {await client.sessions.create({})throw new Error("Expected request to fail")} catch (error) {expect(isUnauthorizedError(error)).toBe(true)}})test("sessions.history decodes SessionNotFoundError", async () => {const client = OpenCode.make({baseUrl: "http://localhost:3000",fetch: async () =>Response.json({ _tag: "SessionNotFoundError", sessionID: "ses_missing", message: "Session not found" },{ status: 404 },),})try {await client.sessions.history({ sessionID: "ses_missing" })throw new Error("Expected request to fail")} catch (error) {expect(isSessionNotFoundError(error)).toBe(true)}})const session = {data: {id: "ses_test",projectID: "project",cost: 0,tokens: {input: 1,output: 2,reasoning: 3,cache: { read: 4, write: 5 },},time: {created: 1_717_171_717_000,updated: 1_717_171_717_000,},title: "Test",location: { directory: "/tmp/project" },},}const admission = {data: {admittedSeq: 0,id: "msg_test",sessionID: "ses_test",prompt: { text: "Hello" },delivery: "steer",timeCreated: 1_717_171_717_000,},}const modelSwitchedMessage = {id: "msg_model",type: "model-switched",time: { created: 1_717_171_717_000 },model: { id: "claude", providerID: "anthropic" },}const modelSwitchedEvent = {id: "evt_model",type: "session.next.model.switched",durable: { aggregateID: "ses_test", seq: 1, version: 1 },data: {timestamp: 1_717_171_717_000,sessionID: "ses_test",messageID: "msg_model",model: { id: "claude", providerID: "anthropic" },},}